Skip to content

Commit 54ea59c

Browse files
authored
chore(hset_family): Add mutable functions to listpack wrapper (#5901)
1 parent c9dcf25 commit 54ea59c

File tree

7 files changed

+98
-109
lines changed

7 files changed

+98
-109
lines changed

src/core/detail/listpack_wrap.cc

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,75 @@ void ListpackWrap::Iterator::Read() {
3434
next_ptr_ = lpNext(lp_, next_ptr_);
3535
}
3636

37+
ListpackWrap::~ListpackWrap() {
38+
DCHECK(!dirty_);
39+
}
40+
41+
uint8_t* ListpackWrap::GetPointer() {
42+
dirty_ = false;
43+
return lp_;
44+
}
45+
3746
ListpackWrap::Iterator ListpackWrap::Find(std::string_view key) const {
47+
if (size() == 0)
48+
return end();
49+
3850
uint8_t* ptr = lpFind(lp_, lpFirst(lp_), (unsigned char*)key.data(), key.size(), 1);
3951
return Iterator{lp_, ptr, intbuf_};
4052
}
4153

54+
bool ListpackWrap::Delete(std::string_view key) {
55+
if (size() == 0)
56+
return false;
57+
58+
uint8_t* ptr = lpFind(lp_, lpFirst(lp_), (unsigned char*)key.data(), key.size(), 1);
59+
if (ptr == nullptr)
60+
return false;
61+
62+
lp_ = lpDeleteRangeWithEntry(lp_, &ptr, 2);
63+
dirty_ = true;
64+
return true;
65+
}
66+
67+
bool ListpackWrap::Insert(std::string_view key, std::string_view value, bool skip_exists) {
68+
uint8_t* vptr;
69+
uint8_t* fptr = lpFirst(lp_);
70+
uint8_t* fsrc = key.empty() ? lp_ : (uint8_t*)key.data();
71+
// if we vsrc is NULL then lpReplace will delete the element, which is not what we want.
72+
// therefore, for an empty val we set it to some other valid address so that lpReplace
73+
// will do the right thing and encode empty string instead of deleting the element.
74+
uint8_t* vsrc = value.empty() ? lp_ : (uint8_t*)value.data();
75+
76+
bool updated = false;
77+
if (fptr) {
78+
fptr = lpFind(lp_, fptr, fsrc, key.size(), 1);
79+
if (fptr) {
80+
if (skip_exists)
81+
return false;
82+
83+
// Grab pointer to the value (fptr points to the field)
84+
vptr = lpNext(lp_, fptr);
85+
86+
// Replace value
87+
lp_ = lpReplace(lp_, &vptr, vsrc, value.size());
88+
DCHECK_EQ(0u, lpLength(lp_) % 2);
89+
90+
dirty_ = true;
91+
updated = true;
92+
}
93+
}
94+
95+
if (!updated) {
96+
// Push new field/value pair onto the tail of the listpack.
97+
// TODO: we should at least allocate once for both elements
98+
lp_ = lpAppend(lp_, fsrc, key.size());
99+
lp_ = lpAppend(lp_, vsrc, value.size());
100+
dirty_ = true;
101+
}
102+
103+
return !updated;
104+
}
105+
42106
size_t ListpackWrap::size() const {
43107
return lpLength(lp_) / 2;
44108
}

src/core/detail/listpack_wrap.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ struct ListpackWrap {
1515
using IntBuf = uint8_t[2][24];
1616

1717
public:
18+
~ListpackWrap();
19+
1820
struct Iterator {
1921
using iterator_category = std::forward_iterator_tag;
2022
using difference_type = std::ptrdiff_t;
@@ -46,14 +48,19 @@ struct ListpackWrap {
4648
explicit ListpackWrap(uint8_t* lp) : lp_{lp} {
4749
}
4850

51+
uint8_t* GetPointer(); // Get new updated pointer
4952
Iterator Find(std::string_view key) const; // Linear search
53+
bool Delete(std::string_view key);
54+
bool Insert(std::string_view key, std::string_view value, bool skip_exists);
55+
5056
Iterator begin() const;
5157
Iterator end() const;
5258
size_t size() const; // number of entries
5359

5460
private:
55-
uint8_t* lp_;
56-
mutable IntBuf intbuf_;
61+
uint8_t* lp_; // the listpack itself
62+
mutable IntBuf intbuf_; // buffer for integers decoded to strings
63+
bool dirty_ = false; // whether lp_ was updated, but never retrieved with GetPointer
5764
};
5865

5966
} // namespace dfly::detail

src/server/container_utils.cc

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,17 +272,6 @@ StringMap* GetStringMap(const PrimeValue& pv, const DbContext& db_context) {
272272
return res;
273273
}
274274

275-
optional<string_view> LpFind(uint8_t* lp, string_view key, uint8_t int_buf[]) {
276-
uint8_t* fptr = lpFirst(lp);
277-
DCHECK(fptr);
278-
279-
fptr = lpFind(lp, fptr, (unsigned char*)key.data(), key.size(), 1);
280-
if (!fptr)
281-
return std::nullopt;
282-
uint8_t* vptr = lpNext(lp, fptr);
283-
return LpGetView(vptr, int_buf);
284-
}
285-
286275
string_view LpGetView(uint8_t* lp_it, uint8_t int_buf[]) {
287276
int64_t ele_len = 0;
288277
uint8_t* elem = lpGet(lp_it, &ele_len, int_buf);

src/server/container_utils.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ StringMap* GetStringMap(const PrimeValue& pv, const DbContext& db_context);
7777
// Get string_view from listpack poiner. Intbuf to store integer values as strings.
7878
std::string_view LpGetView(uint8_t* lp_it, uint8_t int_buf[]);
7979

80-
// Find value by key and return stringview to it, otherwise nullopt.
81-
std::optional<std::string_view> LpFind(uint8_t* lp, std::string_view key, uint8_t int_buf[]);
82-
8380
using BlockingResultCb =
8481
std::function<void(Transaction*, EngineShard*, std::string_view /* key */)>;
8582

src/server/hset_family.cc

Lines changed: 18 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -51,63 +51,6 @@ bool IsGoodForListpack(CmdArgList args, const uint8_t* lp) {
5151
}
5252

5353
using container_utils::GetStringMap;
54-
using container_utils::LpFind;
55-
56-
pair<uint8_t*, bool> LpDelete(uint8_t* lp, string_view field) {
57-
uint8_t* fptr = lpFirst(lp);
58-
DCHECK(fptr);
59-
fptr = lpFind(lp, fptr, (unsigned char*)field.data(), field.size(), 1);
60-
if (fptr == NULL) {
61-
return make_pair(lp, false);
62-
}
63-
64-
/* Delete both of the key and the value. */
65-
lp = lpDeleteRangeWithEntry(lp, &fptr, 2);
66-
return make_pair(lp, true);
67-
}
68-
69-
// returns a new pointer to lp. Returns true if field was inserted or false it it already existed.
70-
// skip_exists controls what happens if the field already existed. If skip_exists = true,
71-
// then val does not override the value and listpack is not changed. Otherwise, the corresponding
72-
// value is overridden by val.
73-
pair<uint8_t*, bool> LpInsert(uint8_t* lp, string_view field, string_view val, bool skip_exists) {
74-
uint8_t* vptr;
75-
76-
uint8_t* fptr = lpFirst(lp);
77-
uint8_t* fsrc = field.empty() ? lp : (uint8_t*)field.data();
78-
79-
// if we vsrc is NULL then lpReplace will delete the element, which is not what we want.
80-
// therefore, for an empty val we set it to some other valid address so that lpReplace
81-
// will do the right thing and encode empty string instead of deleting the element.
82-
uint8_t* vsrc = val.empty() ? lp : (uint8_t*)val.data();
83-
84-
bool updated = false;
85-
86-
if (fptr) {
87-
fptr = lpFind(lp, fptr, fsrc, field.size(), 1);
88-
if (fptr) {
89-
if (skip_exists) {
90-
return make_pair(lp, false);
91-
}
92-
/* Grab pointer to the value (fptr points to the field) */
93-
vptr = lpNext(lp, fptr);
94-
updated = true;
95-
96-
/* Replace value */
97-
lp = lpReplace(lp, &vptr, vsrc, val.size());
98-
DCHECK_EQ(0u, lpLength(lp) % 2);
99-
}
100-
}
101-
102-
if (!updated) {
103-
/* Push new field/value pair onto the tail of the listpack */
104-
// TODO: we should at least allocate once for both elements.
105-
lp = lpAppend(lp, fsrc, field.size());
106-
lp = lpAppend(lp, vsrc, val.size());
107-
}
108-
109-
return make_pair(lp, !updated);
110-
}
11154

11255
struct HMapWrap {
11356
private:
@@ -263,12 +206,13 @@ OpStatus OpIncrBy(const OpArgs& op_args, string_view key, string_view field, Inc
263206
unsigned enc = pv.Encoding();
264207

265208
if (enc == kEncodingListPack) {
266-
uint8_t intbuf[LP_INTBUF_SIZE];
267-
uint8_t* lp = (uint8_t*)pv.RObjPtr();
209+
detail::ListpackWrap lw{static_cast<uint8_t*>(pv.RObjPtr())};
268210
optional<string_view> res;
269211

270-
if (!add_res.is_new)
271-
res = LpFind(lp, field, intbuf);
212+
if (!add_res.is_new) {
213+
if (auto it = lw.Find(field); it != lw.end())
214+
res = (*it).second;
215+
}
272216

273217
OpStatus status = IncrementValue(res, param);
274218
if (status != OpStatus::OK) {
@@ -279,14 +223,14 @@ OpStatus OpIncrBy(const OpArgs& op_args, string_view key, string_view field, Inc
279223
double new_val = get<double>(*param);
280224
char buf[128];
281225
char* str = RedisReplyBuilder::FormatDouble(new_val, buf, sizeof(buf));
282-
lp = LpInsert(lp, field, str, false).first;
226+
lw.Insert(field, str, false);
283227
} else { // integer increment
284228
int64_t new_val = get<int64_t>(*param);
285229
absl::AlphaNum an(new_val);
286-
lp = LpInsert(lp, field, an.Piece(), false).first;
230+
lw.Insert(field, an.Piece(), false);
287231
}
288232

289-
pv.SetRObjPtr(lp);
233+
pv.SetRObjPtr(lw.GetPointer());
290234
} else {
291235
DCHECK_EQ(enc, kEncodingStrMap2);
292236
StringMap* sm = GetStringMap(pv, op_args.db_cntx);
@@ -387,19 +331,13 @@ OpResult<uint32_t> OpDel(const OpArgs& op_args, string_view key, CmdArgList valu
387331
unsigned enc = pv.Encoding();
388332

389333
if (enc == kEncodingListPack) {
390-
uint8_t* lp = (uint8_t*)pv.RObjPtr();
391-
for (auto s : values) {
392-
auto res = LpDelete(lp, s);
393-
if (res.second) {
334+
detail::ListpackWrap lw{static_cast<uint8_t*>(pv.RObjPtr())};
335+
for (string_view s : values) {
336+
if (lw.Delete(s))
394337
++deleted;
395-
lp = res.first;
396-
if (lpLength(lp) == 0) {
397-
key_remove = true;
398-
break;
399-
}
400-
}
401338
}
402-
pv.SetRObjPtr(lp);
339+
pv.SetRObjPtr(lw.GetPointer());
340+
key_remove = lw.size() == 0;
403341
} else {
404342
DCHECK_EQ(enc, kEncodingStrMap2);
405343
StringMap* sm = GetStringMap(pv, op_args.db_cntx);
@@ -505,17 +443,16 @@ OpResult<uint32_t> OpSet(const OpArgs& op_args, string_view key, CmdArgList valu
505443
unsigned created = 0;
506444

507445
if (lp) {
508-
bool inserted;
509446
size_t malloc_reserved = zmalloc_size(lp);
510447
size_t min_sz = EstimateListpackMinBytes(values);
511448
if (min_sz > malloc_reserved) {
512449
lp = (uint8_t*)zrealloc(lp, min_sz);
513450
}
451+
detail::ListpackWrap lw{lp};
514452
for (size_t i = 0; i < values.size(); i += 2) {
515-
tie(lp, inserted) = LpInsert(lp, ArgS(values, i), ArgS(values, i + 1), op_sp.skip_if_exists);
516-
created += inserted;
453+
created += lw.Insert(values[i], values[i + 1], op_sp.skip_if_exists);
517454
}
518-
pv.SetRObjPtr(lp);
455+
pv.SetRObjPtr(lw.GetPointer());
519456
} else {
520457
DCHECK_EQ(kEncodingStrMap2, pv.Encoding()); // Dictionary
521458
StringMap* sm = GetStringMap(pv, op_args.db_cntx);
@@ -1102,10 +1039,8 @@ int32_t HSetFamily::FieldExpireTime(const DbContext& db_context, const PrimeValu
11021039
DCHECK_EQ(OBJ_HASH, pv.ObjType());
11031040

11041041
if (pv.Encoding() == kEncodingListPack) {
1105-
uint8_t intbuf[LP_INTBUF_SIZE];
1106-
uint8_t* lp = (uint8_t*)pv.RObjPtr();
1107-
optional<string_view> res = LpFind(lp, field, intbuf);
1108-
return res ? -1 : -3;
1042+
detail::ListpackWrap lw{static_cast<uint8_t*>(pv.RObjPtr())};
1043+
return lw.Find(field) == lw.end() ? -3 : -1;
11091044
} else {
11101045
StringMap* string_map = (StringMap*)pv.RObjPtr();
11111046
string_map->set_time(MemberTimeSeconds(db_context.time_now_ms));

src/server/search/doc_accessors.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,13 @@ std::optional<BaseAccessor::StringList> BaseAccessor::GetTags(std::string_view a
151151

152152
std::optional<BaseAccessor::StringList> ListPackAccessor::GetStrings(
153153
string_view active_field) const {
154-
auto strsv = container_utils::LpFind(lp_, active_field, intbuf_[0].data());
155-
return strsv.has_value() ? StringList{*strsv} : StringList{};
154+
auto it = lw_.Find(active_field);
155+
return it != lw_.end() ? StringList{(*it).second} : StringList{};
156156
}
157157

158158
SearchDocData ListPackAccessor::Serialize(const search::Schema& schema) const {
159159
SearchDocData out{};
160-
detail::ListpackWrap lw{lp_};
161-
for (const auto [key, value] : lw) {
160+
for (const auto [key, value] : lw_) {
162161
if (auto field_value = ExtractSortableValue(schema, key, value); field_value) {
163162
out[key] = std::move(field_value).value();
164163
}
@@ -399,7 +398,7 @@ unique_ptr<BaseAccessor> GetAccessor(const DbContext& db_cntx, const PrimeValue&
399398
}
400399

401400
if (pv.Encoding() == kEncodingListPack) {
402-
auto ptr = reinterpret_cast<ListPackAccessor::LpPtr>(pv.RObjPtr());
401+
auto ptr = reinterpret_cast<uint8_t*>(pv.RObjPtr());
403402
return make_unique<ListPackAccessor>(ptr);
404403
} else {
405404
auto* sm = container_utils::GetStringMap(pv, db_cntx);

src/server/search/doc_accessors.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <string>
1111
#include <utility>
1212

13+
#include "core/detail/listpack_wrap.h"
1314
#include "core/json/json_object.h"
1415
#include "core/search/search.h"
1516
#include "core/search/vector_utils.h"
@@ -40,17 +41,14 @@ struct BaseAccessor : public search::DocumentAccessor {
4041

4142
// Accessor for hashes stored with listpack
4243
struct ListPackAccessor : public BaseAccessor {
43-
using LpPtr = uint8_t*;
44-
45-
explicit ListPackAccessor(LpPtr ptr) : lp_{ptr} {
44+
explicit ListPackAccessor(uint8_t* ptr /* listpack ptr */) : lw_{ptr} {
4645
}
4746

4847
std::optional<StringList> GetStrings(std::string_view field) const override;
4948
SearchDocData Serialize(const search::Schema& schema) const override;
5049

5150
private:
52-
mutable std::array<uint8_t, 33> intbuf_[2];
53-
LpPtr lp_;
51+
detail::ListpackWrap lw_;
5452
};
5553

5654
// Accessor for hashes stored with StringMap

0 commit comments

Comments
 (0)