Skip to content

Commit 9da231d

Browse files
committed
feat: add Scan
1 parent 9ae71e5 commit 9da231d

File tree

2 files changed

+193
-136
lines changed

2 files changed

+193
-136
lines changed

src/core/intrusive_string_set.h

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ class IntrusiveStringList {
169169
return res;
170170
}
171171

172+
bool Empty() {
173+
return start_;
174+
}
175+
172176
ISLEntry Emplace(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
173177
return Insert(ISLEntry::Create(key, ttl_sec));
174178
}
@@ -181,19 +185,24 @@ class IntrusiveStringList {
181185
}
182186

183187
bool Erase(std::string_view str) {
188+
auto cond = [str](const ISLEntry e) { return str == e.Key(); };
189+
return Erase(cond);
190+
}
191+
192+
template <class T, std::enable_if_t<std::is_invocable_v<T, ISLEntry>>* = nullptr>
193+
bool Erase(const T& cond) {
184194
if (!start_) {
185195
return false;
186196
}
187-
auto it = start_;
188-
if (it.Key() == str) {
197+
198+
if (auto it = start_; cond(it)) {
189199
start_ = it.Next();
190200
ISLEntry::Destroy(it);
191201
return true;
192202
}
193203

194-
auto prev = it;
195-
for (it = it.Next(); it; prev = it, it = it.Next()) {
196-
if (it.Key() == str) {
204+
for (auto prev = start_, it = start_.Next(); it; prev = it, it = it.Next()) {
205+
if (cond(it)) {
197206
prev.SetNext(it.Next());
198207
ISLEntry::Destroy(it);
199208
return true;
@@ -202,6 +211,25 @@ class IntrusiveStringList {
202211
return false;
203212
}
204213

214+
template <class T, std::enable_if_t<std::is_invocable_v<T, std::string_view>>* = nullptr>
215+
bool Scan(const T& cb, uint32_t curr_time) {
216+
for (auto it = start_; it && it.ExpiryTime() < curr_time; it = start_) {
217+
start_ = it.Next();
218+
ISLEntry::Destroy(it);
219+
}
220+
221+
for (auto curr = start_, next = start_; curr; curr = next) {
222+
cb(curr.Key());
223+
next = curr.Next();
224+
for (auto tmp = next; tmp && tmp.ExpiryTime() < curr_time; tmp = next) {
225+
next = next.Next();
226+
ISLEntry::Destroy(tmp);
227+
}
228+
curr.SetNext(next);
229+
}
230+
return start_;
231+
}
232+
205233
private:
206234
ISLEntry start_;
207235
};
@@ -260,6 +288,40 @@ class IntrusiveStringSet {
260288
return res;
261289
}
262290

291+
/**
292+
* stable scanning api. has the same guarantees as redis scan command.
293+
* we avoid doing bit-reverse by using a different function to derive a bucket id
294+
* from hash values. By using msb part of hash we make it "stable" with respect to
295+
* rehashes. For example, with table log size 4 (size 16), entries in bucket id
296+
* 1110 come from hashes 1110XXXXX.... When a table grows to log size 5,
297+
* these entries can move either to 11100 or 11101. So if we traversed with our cursor
298+
* range [0000-1110], it's guaranteed that in grown table we do not need to cover again
299+
* [00000-11100]. Similarly with shrinkage, if a table is shrunk to log size 3,
300+
* keys from 1110 and 1111 will move to bucket 111. Again, it's guaranteed that we
301+
* covered the range [000-111] (all keys in that case).
302+
* Returns: next cursor or 0 if reached the end of scan.
303+
* cursor = 0 - initiates a new scan.
304+
*/
305+
306+
using ItemCb = std::function<void(std::string_view)>;
307+
308+
uint32_t Scan(uint32_t cursor, const ItemCb& cb) {
309+
uint32_t entries_idx = cursor >> (32 - capacity_log_);
310+
311+
// First find the bucket to scan, skip empty buckets.
312+
for (; entries_idx < entries_.size(); ++entries_idx) {
313+
if (entries_[entries_idx].Scan(cb, time_now_)) {
314+
break;
315+
}
316+
}
317+
318+
if (++entries_idx >= entries_.size()) {
319+
return 0;
320+
}
321+
322+
return entries_idx << (32 - capacity_log_);
323+
}
324+
263325
bool Erase(std::string_view str) {
264326
if (entries_.empty())
265327
return false;
@@ -329,7 +391,7 @@ class IntrusiveStringSet {
329391
}
330392

331393
private:
332-
std::uint32_t capacity_log_ = 1;
394+
std::uint32_t capacity_log_ = 0;
333395
std::uint32_t size_ = 0; // number of elements in the set.
334396
std::uint32_t time_now_ = 0;
335397

0 commit comments

Comments
 (0)