-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlru_cache.cpp
160 lines (128 loc) · 3.79 KB
/
lru_cache.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright (c) Omar Boukli-Hacene. All rights reserved.
// Distributed under an MIT-style license that can be
// found in the LICENSE file.
// SPDX-License-Identifier: MIT
#include "forfun/lru_cache.hpp"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
namespace forfun::lrucache {
namespace naive {
LRUCache::LRUCache(std::size_t const capacity) noexcept :
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
cache_{std::make_unique<CacheItem[]>(capacity)}, capacity_{capacity}
{
}
[[nodiscard]] auto LRUCache::get(std::size_t const key) noexcept -> int
{
int result{-1};
std::int64_t lowest_tick_count{ticks_};
std::size_t i{0U};
for (; i < size_; ++i)
{
if (cache_[i].key_ == key)
{
result = cache_[i].value_;
cache_[i].ticks_ = ticks_;
break;
}
if (lowest_tick_count >= cache_[i].ticks_)
{
lowest_tick_count = cache_[i].ticks_;
least_recent_idx_ = i;
}
}
for (; i < size_; ++i)
{
if (lowest_tick_count >= cache_[i].ticks_)
{
lowest_tick_count = cache_[i].ticks_;
least_recent_idx_ = i;
}
}
++ticks_;
return result;
}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
auto LRUCache::put(std::size_t const key, int const value) noexcept -> void
{
assert(size_ <= capacity_);
for (std::size_t i{0U}; i < size_; ++i)
{
if (cache_[i].key_ == key)
{
cache_[i].value_ = value;
return;
}
}
if (size_ < capacity_)
{
cache_[size_].key_ = key;
cache_[size_].value_ = value;
cache_[size_].ticks_ = ticks_;
++size_;
return;
}
cache_[least_recent_idx_].key_ = key;
cache_[least_recent_idx_].value_ = value;
cache_[least_recent_idx_].ticks_ = ticks_;
}
} // namespace naive
namespace stl {
LRUCache::LRUCache(std::size_t const capacity) noexcept : capacity_{capacity}
{
lookup_.reserve(capacity);
}
[[nodiscard]] auto LRUCache::get(std::size_t const key) noexcept -> int
{
auto const existing_key_iter{lookup_.find(key)};
if (existing_key_iter == lookup_.end())
{
return -1;
}
cache_.splice(cache_.end(), cache_, existing_key_iter->second);
return existing_key_iter->second->second;
}
auto LRUCache::put(std::size_t const key, int const value) noexcept -> void
{
assert(size_ <= capacity_);
assert(size_ == lookup_.size());
assert(size_ == cache_.size());
// Update if key exists.
auto const iter_existing_key{lookup_.find(key)};
if (iter_existing_key != lookup_.end())
{
iter_existing_key->second->second = value;
return;
}
// Add new if cache is not full.
auto const cache_end{cache_.end()};
if (size_ < capacity_)
{
auto const emplaced_key_iter{cache_.emplace(cache_end, key, value)};
assert(cache_.end() == cache_end);
[[maybe_unused]] auto const new_key_insert_result{
lookup_.insert({key, emplaced_key_iter})
};
assert(new_key_insert_result.second);
++size_;
return;
}
// Replace oldest key if cache is full.
[[maybe_unused]] auto const num_elements_removed{
lookup_.erase(cache_.begin()->first)
};
assert(num_elements_removed == 1U);
cache_.splice(cache_end, cache_, cache_.begin());
[[maybe_unused]] auto const replacement_key_insert_result{
lookup_.insert({key, std::prev(cache_end)})
};
assert(replacement_key_insert_result.second);
assert(cache_.end() == cache_end);
cache_.back().first = key;
cache_.back().second = value;
}
} // namespace stl
} // namespace forfun::lrucache