Skip to content

Commit a8c935f

Browse files
authored
Merge pull request #70 from jagerman/set-erase-fix
Fix bug in set erasing causing config dirtying
2 parents c462f85 + bce09b5 commit a8c935f

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed

include/session/config/base.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,13 @@ class ConfigBase : public ConfigSig {
438438
/// Ouputs:
439439
/// - `bool` -- True if the value was inserted, false otherwise
440440
bool insert_if_missing(config::scalar&& value) {
441+
// If the config isn't marked dirty then we can do a first pass to see if the value is
442+
// already there: if it is, we want to short-circuit marking the config dirty because
443+
// this insertion is a no-op. If the config is *already* dirty then there's no point in
444+
// doing this possibly extra check, because even if we don't change anything the config
445+
// is *already* dirty.
441446
if (!_conf.is_dirty())
442-
if (auto current = get_clean<config::set>(); current && current->count(value))
447+
if (auto current = get_clean<config::set>(); current && current->contains(value))
443448
return false;
444449

445450
get_dirty<config::set>().insert(std::move(value));
@@ -456,8 +461,10 @@ class ConfigBase : public ConfigSig {
456461
/// Outputs:
457462
/// - `bool` -- True if an element was erased, false otherwise
458463
bool set_erase_impl(const config::scalar& value) {
464+
// See comment in `insert_if_missing`, above. This is the same short-circuiting logic
465+
// (but with a negated condition because this is erasing instead of inserting).
459466
if (!_conf.is_dirty())
460-
if (auto current = get_clean<config::set>(); current && !current->count(value))
467+
if (auto current = get_clean<config::set>(); !(current && current->contains(value)))
461468
return false;
462469

463470
config::dict* data = &_conf.dirty().data();

src/config/internal.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,17 @@ std::optional<std::string> maybe_string(const session::config::dict& d, const ch
149149

150150
uint64_t bitset_from_set_of_int64_or_0(const session::config::set& s) {
151151
uint64_t result = 0;
152-
const size_t bits_available = sizeof(result) * 8;
153-
for (auto it : s) {
154-
auto* val = std::get_if<int64_t>(&it);
152+
constexpr size_t bits_available = sizeof(result) * 8;
153+
for (auto& v : s) {
154+
auto* val = std::get_if<int64_t>(&v);
155155
if (val && (*val >= 0 && *val < bits_available))
156156
result |= (1ULL << *val);
157157
}
158158
return result;
159159
}
160160

161161
void set_int64_set_from_bitset(ConfigBase::DictFieldProxy&& field, uint64_t bitset) {
162-
const size_t bits_available = sizeof(bitset) * 8;
162+
constexpr size_t bits_available = sizeof(bitset) * 8;
163163
for (size_t index = 0; index < bits_available; index++) {
164164
uint64_t bit = bitset & (1ULL << index);
165165
if (bit)

tests/test_config_contacts.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,8 @@ TEST_CASE("Contacts Pro Storage", "[config][contacts][pro]") {
11241124

11251125
session::config::Contacts contacts{std::span<const unsigned char>{seed}, std::nullopt};
11261126

1127+
REQUIRE(contacts.is_clean());
1128+
11271129
// Set the bitsets onto the profile
11281130
{
11291131
auto c = contacts.get_or_construct(
@@ -1133,6 +1135,8 @@ TEST_CASE("Contacts Pro Storage", "[config][contacts][pro]") {
11331135
c.profile_bitset.set(SESSION_PROTOCOL_PRO_PROFILE_FEATURES_PRO_BADGE);
11341136
contacts.set(c);
11351137

1138+
CHECK(contacts.is_dirty());
1139+
11361140
c = contacts.get_or_construct(
11371141
"050000000000000000000000000000000000000000000000000000000000000000"sv);
11381142
CHECK(c.profile_bitset.is_set(SESSION_PROTOCOL_PRO_PROFILE_FEATURES_PRO_BADGE));
@@ -1171,4 +1175,29 @@ TEST_CASE("Contacts Pro Storage", "[config][contacts][pro]") {
11711175
"050000000000000000000000000000000000000000000000000000000000000001"sv);
11721176
CHECK(!c.profile_bitset.is_set(SESSION_PROTOCOL_PRO_PROFILE_FEATURES_PRO_BADGE));
11731177
}
1178+
1179+
CHECK(contacts.needs_push());
1180+
CHECK(contacts.needs_dump());
1181+
1182+
auto [seqno, to_push, obs] = contacts.push();
1183+
// Simulated push:
1184+
contacts.confirm_pushed(seqno, {"fakehash1"});
1185+
// Simulated dump:
1186+
contacts.dump();
1187+
1188+
REQUIRE_FALSE(contacts.needs_push());
1189+
REQUIRE_FALSE(contacts.needs_dump());
1190+
1191+
{
1192+
auto c = contacts.get_or_construct(
1193+
"050000000000000000000000000000000000000000000000000000000000000000"sv);
1194+
CHECK(c.profile_bitset.is_set(SESSION_PROTOCOL_PRO_PROFILE_FEATURES_PRO_BADGE));
1195+
CHECK(c.profile_bitset.is_set(SESSION_PROTOCOL_PRO_PROFILE_FEATURES_ANIMATED_AVATAR));
1196+
// This previously exposed a bug in set_erase_impl where the contact was being dirtied even
1197+
// when nothing was actually being removed.
1198+
contacts.set(c);
1199+
}
1200+
1201+
CHECK_FALSE(contacts.needs_push());
1202+
CHECK_FALSE(contacts.needs_dump());
11741203
}

0 commit comments

Comments
 (0)