Skip to content

Commit ceed85a

Browse files
authored
chore(hset_family): Listpack wrapper (#5889)
1 parent ccdcfc3 commit ceed85a

File tree

6 files changed

+189
-171
lines changed

6 files changed

+189
-171
lines changed

src/core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ add_library(dfly_core allocation_tracker.cc bloom.cc compact_object.cc dense_set
2626
dragonfly_core.cc extent_tree.cc huff_coder.cc
2727
interpreter.cc glob_matcher.cc mi_memory_resource.cc qlist.cc sds_utils.cc
2828
segment_allocator.cc score_map.cc small_string.cc sorted_map.cc task_queue.cc
29-
tx_queue.cc string_set.cc string_map.cc top_keys.cc detail/bitpacking.cc
29+
tx_queue.cc string_set.cc string_map.cc top_keys.cc detail/bitpacking.cc detail/listpack_wrap.cc
3030
page_usage_stats.cc count_min_sketch.cc)
3131

3232
cxx_link(dfly_core base dfly_search_core fibers2 jsonpath

src/core/detail/listpack_wrap.cc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2025, DragonflyDB authors. All rights reserved.
2+
// See LICENSE for licensing terms.
3+
//
4+
#include "core/detail/listpack_wrap.h"
5+
6+
#include "server/container_utils.h"
7+
8+
extern "C" {
9+
#include "redis/listpack.h"
10+
}
11+
12+
namespace dfly::detail {
13+
14+
ListpackWrap::Iterator::Iterator(uint8_t* lp, uint8_t* ptr)
15+
: lp_{lp}, ptr_{ptr}, next_ptr_{nullptr} {
16+
static_assert(sizeof(intbuf_[0]) >= LP_INTBUF_SIZE); // to avoid header dependency
17+
Read();
18+
}
19+
20+
ListpackWrap::Iterator& ListpackWrap::Iterator::operator++() {
21+
ptr_ = next_ptr_;
22+
Read();
23+
return *this;
24+
}
25+
26+
void ListpackWrap::Iterator::Read() {
27+
if (!ptr_)
28+
return;
29+
30+
using container_utils::LpGetView;
31+
key_v_ = LpGetView(ptr_, intbuf_[0]);
32+
next_ptr_ = lpNext(lp_, ptr_);
33+
value_v_ = LpGetView(next_ptr_, intbuf_[1]);
34+
next_ptr_ = lpNext(lp_, next_ptr_);
35+
}
36+
37+
ListpackWrap::Iterator ListpackWrap::Find(std::string_view key) const {
38+
uint8_t* ptr = lpFind(lp_, lpFirst(lp_), (unsigned char*)key.data(), key.size(), 1);
39+
return Iterator{lp_, ptr};
40+
}
41+
42+
size_t ListpackWrap::size() const {
43+
return lpLength(lp_) / 2;
44+
}
45+
46+
ListpackWrap::Iterator ListpackWrap::begin() const {
47+
return Iterator{lp_, lpFirst(lp_)};
48+
}
49+
50+
ListpackWrap::Iterator ListpackWrap::end() const {
51+
return Iterator{lp_, nullptr};
52+
}
53+
54+
bool ListpackWrap::Iterator::operator==(const Iterator& other) const {
55+
return lp_ == other.lp_ && ptr_ == other.ptr_;
56+
}
57+
} // namespace dfly::detail

src/core/detail/listpack_wrap.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2025, DragonflyDB authors. All rights reserved.
2+
// See LICENSE for licensing terms.
3+
//
4+
#pragma once
5+
6+
#include <cstdint>
7+
#include <cstdio>
8+
#include <string_view>
9+
10+
namespace dfly::detail {
11+
12+
// Wrapper around map data structure based on listpack
13+
struct ListpackWrap {
14+
struct Iterator {
15+
using iterator_category = std::forward_iterator_tag;
16+
using difference_type = std::ptrdiff_t;
17+
using value_type = std::pair<std::string_view, std::string_view>;
18+
19+
Iterator(uint8_t* lp, uint8_t* ptr);
20+
Iterator(const Iterator&) = delete; // self-referential with intbuf
21+
Iterator(Iterator&&) = delete; // self-referential with intbuf
22+
Iterator& operator=(Iterator&&) = delete;
23+
Iterator& operator=(const Iterator&) = delete;
24+
25+
Iterator& operator++();
26+
27+
value_type operator*() const {
28+
return {key_v_, value_v_};
29+
}
30+
31+
bool operator==(const Iterator& other) const;
32+
33+
bool operator!=(const Iterator& other) const {
34+
return !(operator==(other));
35+
}
36+
37+
private:
38+
void Read(); // Read next entry at ptr and determine next_ptr
39+
40+
uint8_t *lp_ = nullptr, *ptr_ = nullptr, *next_ptr_ = nullptr;
41+
std::string_view key_v_, value_v_;
42+
uint8_t intbuf_[2][24];
43+
};
44+
45+
explicit ListpackWrap(uint8_t* lp) : lp_{lp} {
46+
}
47+
48+
Iterator Find(std::string_view key) const; // Linear search
49+
Iterator begin() const;
50+
Iterator end() const;
51+
size_t size() const; // number of entries
52+
53+
private:
54+
uint8_t* lp_;
55+
};
56+
57+
} // namespace dfly::detail

src/server/container_utils.cc

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "base/flags.h"
77
#include "base/logging.h"
8+
#include "core/detail/listpack_wrap.h"
89
#include "core/qlist.h"
910
#include "core/sorted_map.h"
1011
#include "core/string_map.h"
@@ -243,14 +244,8 @@ bool IterateMap(const PrimeValue& pv, const IterateKVFunc& func) {
243244
bool finished = true;
244245

245246
if (pv.Encoding() == kEncodingListPack) {
246-
uint8_t k_intbuf[LP_INTBUF_SIZE], v_intbuf[LP_INTBUF_SIZE];
247-
uint8_t* lp = (uint8_t*)pv.RObjPtr();
248-
uint8_t* fptr = lpFirst(lp);
249-
while (fptr) {
250-
string_view key = LpGetView(fptr, k_intbuf);
251-
fptr = lpNext(lp, fptr);
252-
string_view val = LpGetView(fptr, v_intbuf);
253-
fptr = lpNext(lp, fptr);
247+
detail::ListpackWrap lw{static_cast<uint8_t*>(pv.RObjPtr())};
248+
for (const auto [key, val] : lw) {
254249
if (!func(ContainerEntry{key.data(), key.size()}, ContainerEntry{val.data(), val.size()})) {
255250
finished = false;
256251
break;

0 commit comments

Comments
 (0)