Skip to content

Commit 82809aa

Browse files
committed
UPD | cache
1 parent fb8fdd9 commit 82809aa

6 files changed

Lines changed: 243 additions & 35 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ set(MANAPIHTTP_HTTP_FILES
633633
src/ManapiFetch.cpp
634634
src/json/ManapiJsonMask.cpp
635635
src/http/ManapiHttpPool.cpp
636+
include/cache/ManapiLRU.hpp
636637
src/ManapiTimerPool.cpp
637638
src/http/ManapiSite.cpp
638639
src/http/ManapiHttpConfig.cpp

include/cache/ManapiLRU.hpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <unordered_map>
5+
#include <cstddef>
6+
7+
#include "./../ManapiErrors.hpp"
8+
#include "./../ManapiUtils.hpp"
9+
#include "./../std/ManapiChain.hpp"
10+
11+
namespace manapi {
12+
template<typename K, typename V>
13+
class lru_cache {
14+
typedef typename std::pair<K, V> key_value_pair_t;
15+
16+
typedef typename manapi::chain<key_value_pair_t>::iterator list_iterator_t;
17+
18+
struct item_t {
19+
list_iterator_t item;
20+
std::size_t weight;
21+
};
22+
public:
23+
lru_cache () : lru_cache(0) {}
24+
25+
lru_cache (std::size_t max_size) {
26+
this->m_max = max_size;
27+
this->m_used = 0;
28+
}
29+
30+
MANAPIHTTP_NODISCARD std::size_t size () const MANAPIHTTP_NOEXCEPT {
31+
return this->m_items.size();
32+
}
33+
34+
MANAPIHTTP_NODISCARD bool contains (const K &key) const MANAPIHTTP_NOEXCEPT {
35+
return this->m_items.contains(key);
36+
}
37+
38+
void put (const K &key, const V &value, std::size_t weight) {
39+
auto it = this->m_items.find(key);
40+
if (it == this->m_items.end()) {
41+
this->m_list.push_front(key_value_pair_t (key, value));
42+
try {
43+
item_t item (this->m_list.begin(), weight);
44+
this->m_items.insert({key, std::move(item)});
45+
}
46+
catch (...) {
47+
this->m_list.pop_front();
48+
std::rethrow_exception(std::current_exception());
49+
}
50+
}
51+
else {
52+
it->second.item->second = value; // malloc can be only here
53+
54+
std::unique_ptr<chain_item<key_value_pair_t>> un;
55+
this->m_used -= it->second.weight;
56+
this->m_list.erase(it->second.item, un);
57+
assert(!!un);
58+
this->m_list.push_front_chain(std::move(un));
59+
it->second.item = this->m_list.begin();
60+
it->second.weight = weight;
61+
}
62+
63+
this->m_used += weight;
64+
65+
this->cleanup();
66+
}
67+
68+
MANAPIHTTP_NODISCARD manapi::status_or<V*> get (const K &key) MANAPIHTTP_NOEXCEPT {
69+
auto it = this->m_items.find(key);
70+
if (it == this->m_items.end()) {
71+
return manapi::status_not_found();
72+
}
73+
74+
std::unique_ptr<chain_item<key_value_pair_t>> un;
75+
this->m_list.erase(it->second.item, un);
76+
assert(!!un);
77+
this->m_list.push_front_chain(std::move(un));
78+
it->second.item = this->m_list.begin();
79+
80+
return &it->second.item->second;
81+
}
82+
83+
void remove (const K &key) MANAPIHTTP_NOEXCEPT {
84+
auto it = this->m_items.find(key);
85+
if (it != this->m_items.end()) {
86+
this->m_list.erase(it->second.item);
87+
this->m_used -= it->second.weight;
88+
this->m_items.erase(it);
89+
}
90+
}
91+
92+
void clear () MANAPIHTTP_NOEXCEPT {
93+
this->m_used = 0;
94+
this->m_items.clear();
95+
this->m_list.clear();
96+
}
97+
98+
MANAPIHTTP_NODISCARD std::size_t used () const MANAPIHTTP_NOEXCEPT {
99+
return this->m_used;
100+
}
101+
102+
void max (std::size_t m) {
103+
this->m_max = m;
104+
this->cleanup();
105+
}
106+
107+
MANAPIHTTP_NODISCARD std::size_t max () MANAPIHTTP_NOEXCEPT {
108+
return this->m_max;
109+
}
110+
111+
void cleanup () MANAPIHTTP_NOEXCEPT {
112+
while (this->m_used > this->m_max) {
113+
auto it = this->m_list.rbegin();
114+
auto data = this->m_items.extract(it->first);
115+
assert(!data.empty());
116+
this->m_used -= data.mapped().weight;
117+
this->m_list.erase(it);
118+
}
119+
}
120+
private:
121+
std::size_t m_max;
122+
123+
std::size_t m_used;
124+
125+
manapi::chain<key_value_pair_t> m_list;
126+
127+
std::unordered_map<K, item_t> m_items;
128+
};
129+
}

include/std/ManapiChain.hpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ namespace manapi {
4646
return &this->src_->src;
4747
}
4848

49+
const value_type &operator*() const {
50+
return this->src_->src;
51+
}
52+
53+
const pointer operator->() const {
54+
return &this->src_->src;
55+
}
56+
4957
bool operator==(const chain_iterator &_n) const {
5058
return this->src_ == _n.src_;
5159
}
@@ -140,80 +148,86 @@ namespace manapi {
140148
void push_back (value_type &&n) {
141149
auto _n = std::make_unique<chain_item<value_type>>( std::move(n), nullptr, nullptr);
142150
this->push_back_chain(std::move(_n));
143-
++this->s_;
144151
}
145152

146153
void push_front (value_type &&n) {
147154
auto _n = std::make_unique<chain_item<value_type>>( std::move(n), nullptr, nullptr);
148155
this->push_front_chain(std::move(_n));
149-
++this->s_;
150156
}
151157

152158
void push_back (const value_type &n) {
153159
auto _n = std::make_unique<chain_item<value_type>>( n, nullptr, nullptr);
154160
this->push_back_chain(std::move(_n));
155-
++this->s_;
156161
}
157162

158163
void push_front (const value_type &n) {
159164
auto _n = std::make_unique<chain_item<value_type>>( n, nullptr, nullptr);
160165
this->push_front_chain(std::move(_n));
161-
++this->s_;
162166
}
163167

164168
void push_back_chain (chain_item_un n) {
165169
if (this->last_ == nullptr) {
166170
this->src_ = std::move(n);
167171
this->last_ = this->src_.get();
172+
++this->s_;
168173
return;
169174
}
170175

171176
this->last_->next = std::move(n);
172177
this->last_->next->prev = this->last_;
173178
this->last_ = this->last_->next.get();
179+
++this->s_;
174180
}
175181

176182
void push_front_chain (chain_item_un n) {
177183
if (this->src_ == nullptr) {
178184
this->last_ = n.get();
179185
this->src_ = std::move(n);
186+
++this->s_;
180187
return;
181188
}
182189

183190
this->src_->prev = n.get();
184191
n->next = std::move(this->src_);
185192
this->src_ = std::move(n);
193+
++this->s_;
186194
}
187195

188-
void erase (chain_item<value_type> *n) {
196+
chain_item_un erase (chain_item<value_type> *n) {
189197
auto ss = this->size();
190198

191199
if (!ss) {
192-
return;
200+
return nullptr;
193201
}
194202

203+
chain_item_un m;
204+
195205
if (ss == 1) {
196206
if (this->src_.get() == n) {
207+
m = std::move(this->src_);
208+
197209
this->src_ = nullptr;
198210
this->last_ = nullptr;
199211

200212
--this->s_;
201213
}
202-
return;
214+
return std::move(m);
203215
}
204216

205217
auto next = std::move(n->next);
206218
auto prev = n->prev;
207219

208220
if (!next && !prev) {
209-
// not exists
210-
return;
221+
// doesn't exist
222+
return nullptr;
211223
}
212224

213225
if (!prev) {
226+
m = std::move(this->src_);
214227
this->src_ = std::move(next);
215228
}
216229
else {
230+
m = std::move(prev->next);
217231
prev->next = std::move(next);
218232
}
219233

@@ -225,6 +239,14 @@ namespace manapi {
225239
}
226240

227241
--this->s_;
242+
return std::move(m);
243+
}
244+
245+
iterator erase (iterator n, chain_item_un &u) {
246+
if (!n.src_) { return n; }
247+
auto next = n.src_->next.get();
248+
u = this->erase(n.src_);
249+
return iterator{next};
228250
}
229251

230252
iterator erase (iterator n) {

main.cpp

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <ManapiHttp.hpp>
44
#include <ManapiInitTools.hpp>
5+
6+
#include "cache/ManapiLRU.hpp"
57
#include "ext/pq/AsyncPostgreClient.hpp"
68
#include "ext/pq/AsyncPostgrePool.hpp"
79
#define FOLDER "/home/Timur/Downloads/anime-main/"
@@ -18,36 +20,27 @@ int main() {
1820
ctx->run(0, [server_ctx] (auto cb) -> void {
1921
using http = manapi::net::http::server;
2022

23+
manapi::lru_cache<std::string, std::string> caching (10);
24+
2125
auto route = manapi::net::http::server::create(server_ctx).unwrap();
2226
auto db = manapi::ext::pq::db::create().unwrap();
2327

24-
route.GET ("/", [db] (http::req &req, http::resp &resp) mutable
28+
route.GET ("/", [db, &caching] (http::req &req, http::resp &resp) mutable
2529
-> manapi::future<> {
26-
27-
manapi::ext::pq::result res = manapi::unwrap(co_await db->exec(manapi::ext::pq::kSlave, "SELECT * FROM test;"));
28-
std::string content;
29-
for (auto row : res) {
30-
content += row["text"].as<std::string>() + "\n";
30+
if (req.contains_get_param("k") && req.contains_get_param("v")) {
31+
auto key = req.get("k").unwrap();
32+
auto val = req.get("v").unwrap();
33+
caching.put(std::string{key}, std::string(val), val.size());
34+
co_return resp.text("OK!").unwrap();
35+
}
36+
else if (req.contains_get_param("k")) {
37+
auto key = req.get("k").unwrap();
38+
auto res = caching.get(std::string{key});
39+
co_return resp.text(*res.unwrap()).unwrap();
40+
}
41+
else {
42+
co_return resp.text("k param").unwrap();
3143
}
32-
33-
resp.replacers({
34-
{"data", std::move(content)}
35-
}).unwrap();
36-
37-
co_return resp.file("../test.html").unwrap();
38-
}).unwrap();
39-
40-
route.POST ("/", [db] (http::req &req, http::resp &resp) mutable -> manapi::future<> {
41-
std::string name;
42-
manapi::unwrap(co_await req.form([&name] (std::string key) {
43-
if (key != "text") {
44-
throw std::runtime_error ("Invalid param");
45-
}
46-
return manapi::net::formdata_recv::save_string(&name, 500);
47-
}));
48-
manapi::ext::pq::result res = manapi::unwrap(co_await db->execl(manapi::ext::pq::kMaster, "INSERT INTO test (text) VALUES ($1);",
49-
manapi::ctokens::timeout(2500), name));
50-
co_return resp.json({{"code", 0}, {"msg", "OK"}}).unwrap();
5144
}).unwrap();
5245

5346
manapi::async::run ([route, db] () mutable -> manapi::future<> {

tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ add_compile_options($<$<CXX_COMPILER_ID:GNU>:-fmerge-all-constants>)
66

77
set(MANAPIHTTP_TEST_TOOL_FILES "utest.h" "tools.hpp")
88

9-
set(MANAPIHTTP_TESTS_SOURCES test_std.cpp test_bigint.cpp test_timer.cpp test_fs.cpp test_fs_io.cpp test_http_and_fetch.cpp test_json.cpp test_json_masks.cpp test_parse.cpp test_http_and_fetch_keep_alive.cpp test_function.cpp)
9+
set(MANAPIHTTP_TESTS_SOURCES test_std.cpp test_bigint.cpp test_timer.cpp test_fs.cpp test_fs_io.cpp test_http_and_fetch.cpp test_json.cpp test_json_masks.cpp test_parse.cpp test_http_and_fetch_keep_alive.cpp test_function.cpp test_cache.cpp)
1010
foreach(test ${MANAPIHTTP_TESTS_SOURCES})
1111
set(test_name ${test}_ctest)
1212
include_directories(${test_name} "../include" "${CMAKE_BINARY_DIR}/include")

tests/test_cache.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "cache/ManapiLRU.hpp"
2+
3+
#include "./utest.h"
4+
5+
UTEST(cache, lru1_put) {
6+
manapi::lru_cache<std::string, std::string> a (10000);
7+
a.put("hello", "world", 5);
8+
a.put("hello2", "world", 5);
9+
ASSERT_TRUE(*a.get("hello").unwrap() == "world");
10+
ASSERT_TRUE(*a.get("hello2").unwrap() == "world");
11+
ASSERT_TRUE(a.used() == 10);
12+
}
13+
14+
UTEST(cache, lru_remove) {
15+
manapi::lru_cache<std::string, std::string> a (10000);
16+
a.put("hello", "world", 5);
17+
a.put("hello2", "world", 5);
18+
a.remove("hello");
19+
ASSERT_TRUE(!a.get("hello").ok());
20+
ASSERT_TRUE(*a.get("hello2").unwrap() == "world");
21+
ASSERT_TRUE(a.used() == 5);
22+
}
23+
24+
UTEST(cache, lru_overflow) {
25+
manapi::lru_cache<std::string, std::string> a (10);
26+
a.put("hello", "world", 5);
27+
a.put("hello2", "world", 5);
28+
a.put("hello3", "world", 5);
29+
ASSERT_TRUE(!a.get("hello").ok());
30+
ASSERT_TRUE(a.get("hello2").ok());
31+
ASSERT_TRUE(a.get("hello3").ok());
32+
ASSERT_TRUE(a.used() == 10);
33+
}
34+
35+
UTEST(cache, lru_priorities) {
36+
manapi::lru_cache<std::string, std::string> a (10);
37+
a.put("hello", "world", 5);
38+
a.put("hello2", "world", 5);
39+
ASSERT_TRUE(a.get("hello").ok());
40+
a.put("hello3", "world", 5);
41+
ASSERT_TRUE(a.get("hello").ok());
42+
ASSERT_TRUE(!a.get("hello2").ok());
43+
ASSERT_TRUE(a.get("hello3").ok());
44+
ASSERT_TRUE(a.used() == 10);
45+
}
46+
47+
UTEST(cache, lru_reset) {
48+
manapi::lru_cache<std::string, std::string> a (10);
49+
a.put("hello", "world", 5);
50+
a.put("hello2", "world", 5);
51+
ASSERT_TRUE(a.get("hello").ok());
52+
a.put("hello3", "world", 5);
53+
ASSERT_TRUE(a.get("hello").ok());
54+
ASSERT_TRUE(!a.get("hello2").ok());
55+
ASSERT_TRUE(a.get("hello3").ok());
56+
ASSERT_TRUE(a.used() == 10);
57+
a.clear();
58+
ASSERT_TRUE(!a.get("hello").ok());
59+
ASSERT_TRUE(!a.get("hello3").ok());
60+
ASSERT_TRUE(a.used() == 0);
61+
}
62+
63+
UTEST_MAIN();

0 commit comments

Comments
 (0)