Skip to content

Commit

Permalink
Implement the set algebra functions on Mixed
Browse files Browse the repository at this point in the history
This potentially makes them slightly slower, but is an absolutely massive size
reduction and simplifies the code somewhat.
  • Loading branch information
tgoyne committed Oct 31, 2023
1 parent 349b4d6 commit 2084261
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 360 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

### Internals
* REALM_[ATMU]SAN cmake flags no longer override compilation options and can be combined with Debug|RelWithDebInfo|etc. build types. Rel[ATMU]SAN build type shortcuts are now all slightly optimized debug-based builds with sanitizers. REALM_ASAN now works with msvc (2019/2022) builds. ([PR #6911](https://github.com/realm/realm-core/pull/6911))
* Rework the implemenatation of the set algrebra functions on Set<T> to reduce the compiled size.

----------------------------------------------

Expand Down
22 changes: 21 additions & 1 deletion src/realm/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ class CollectionBase {
return get_table()->get_column_name(get_col_key());
}

// These are shadowed by typed versions in subclasses
using value_type = Mixed;
CollectionIterator<CollectionBase> begin() const;
CollectionIterator<CollectionBase> end() const;

protected:
friend class Transaction;
CollectionBase() noexcept = default;
Expand Down Expand Up @@ -679,7 +684,12 @@ struct CollectionIterator {

pointer operator->() const
{
m_val = m_list->get(m_ndx);
if constexpr (std::is_same_v<L, CollectionBase>) {
m_val = m_list->get_any(m_ndx);
}
else {
m_val = m_list->get(m_ndx);
}
return &m_val;
}

Expand Down Expand Up @@ -765,6 +775,16 @@ struct CollectionIterator {
size_t m_ndx = size_t(-1);
};


inline CollectionIterator<CollectionBase> CollectionBase::begin() const
{
return CollectionIterator<CollectionBase>(this, 0);
}
inline CollectionIterator<CollectionBase> CollectionBase::end() const
{
return CollectionIterator<CollectionBase>(this, size());
}

namespace _impl {
size_t get_collection_size_from_ref(ref_type, Allocator& alloc);
}
Expand Down
4 changes: 1 addition & 3 deletions src/realm/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,7 @@ class Lst final : public CollectionBaseImpl<LstBase> {

static Mixed unresolved_to_null(Mixed value) noexcept
{
if (value.is_type(type_TypedLink) && value.is_unresolved_link())
return Mixed{};
return value;
return value.is_unresolved_link() ? Mixed{} : value;
}
T do_get(size_t ndx, const char* msg) const;
};
Expand Down
40 changes: 10 additions & 30 deletions src/realm/object-store/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,72 +246,52 @@ std::pair<size_t, bool> Set::insert<Obj>(Obj obj)

bool Set::is_subset_of(const Collection& rhs) const
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().is_subset_of(rhs.get_impl());
});
return set_base().is_subset_of(rhs.get_impl());
}

bool Set::is_strict_subset_of(const Collection& rhs) const
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().is_strict_subset_of(rhs.get_impl());
});
return set_base().is_strict_subset_of(rhs.get_impl());
}

bool Set::is_superset_of(const Collection& rhs) const
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().is_superset_of(rhs.get_impl());
});
return set_base().is_superset_of(rhs.get_impl());
}

bool Set::is_strict_superset_of(const Collection& rhs) const
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().is_strict_superset_of(rhs.get_impl());
});
return set_base().is_strict_superset_of(rhs.get_impl());
}

bool Set::intersects(const Collection& rhs) const
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().intersects(rhs.get_impl());
});
return set_base().intersects(rhs.get_impl());
}

bool Set::set_equals(const Collection& rhs) const
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().set_equals(rhs.get_impl());
});
return set_base().set_equals(rhs.get_impl());
}

void Set::assign_intersection(const Collection& rhs)
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().assign_intersection(rhs.get_impl());
});
set_base().assign_intersection(rhs.get_impl());
}

void Set::assign_union(const Collection& rhs)
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().assign_union(rhs.get_impl());
});
set_base().assign_union(rhs.get_impl());
}

void Set::assign_difference(const Collection& rhs)
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().assign_difference(rhs.get_impl());
});
set_base().assign_difference(rhs.get_impl());
}

void Set::assign_symmetric_difference(const Collection& rhs)
{
return dispatch([&](auto t) {
return this->as<std::decay_t<decltype(*t)>>().assign_symmetric_difference(rhs.get_impl());
});
set_base().assign_symmetric_difference(rhs.get_impl());
}

} // namespace realm::object_store
Expand Down
211 changes: 175 additions & 36 deletions src/realm/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,187 @@ void SetBase::clear_repl(Replication* repl) const
repl->set_clear(*this);
}

std::vector<Mixed> SetBase::convert_to_mixed_set(const CollectionBase& rhs)
static std::vector<Mixed> convert_to_set(const CollectionBase& rhs)
{
std::vector<Mixed> mixed;
mixed.reserve(rhs.size());
for (size_t i = 0; i < rhs.size(); i++) {
mixed.push_back(rhs.get_any(i));
}
std::vector<Mixed> mixed(rhs.begin(), rhs.end());
std::sort(mixed.begin(), mixed.end(), SetElementLessThan<Mixed>());
mixed.erase(std::unique(mixed.begin(), mixed.end()), mixed.end());
return mixed;
}

bool SetBase::is_subset_of(const CollectionBase& rhs) const
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return is_subset_of(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return is_subset_of(other_set.begin(), other_set.end());
}

template <class It1, class It2>
bool SetBase::is_subset_of(It1 first, It2 last) const
{
return std::includes(first, last, begin(), end(), SetElementLessThan<Mixed>{});
}

bool SetBase::is_strict_subset_of(const CollectionBase& rhs) const
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return size() != rhs.size() && is_subset_of(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return size() != other_set.size() && is_subset_of(other_set.begin(), other_set.end());
}

bool SetBase::is_superset_of(const CollectionBase& rhs) const
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return is_superset_of(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return is_superset_of(other_set.begin(), other_set.end());
}

template <class It1, class It2>
bool SetBase::is_superset_of(It1 first, It2 last) const
{
return std::includes(begin(), end(), first, last, SetElementLessThan<Mixed>{});
}

bool SetBase::is_strict_superset_of(const CollectionBase& rhs) const
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return size() != rhs.size() && is_superset_of(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return size() != other_set.size() && is_superset_of(other_set.begin(), other_set.end());
}

bool SetBase::intersects(const CollectionBase& rhs) const
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return intersects(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return intersects(other_set.begin(), other_set.end());
}

template <class It1, class It2>
bool SetBase::intersects(It1 first, It2 last) const
{
SetElementLessThan<Mixed> less;
auto it = begin();
while (it != end() && first != last) {
if (less(*it, *first)) {
++it;
}
else if (less(*first, *it)) {
++first;
}
else {
return true;
}
}
return false;
}

bool SetBase::set_equals(const CollectionBase& rhs) const
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return size() == rhs.size() && is_subset_of(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return size() == other_set.size() && is_subset_of(other_set.begin(), other_set.end());
}

void SetBase::assign_union(const CollectionBase& rhs)
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return assign_union(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return assign_union(other_set.begin(), other_set.end());
}

template <class It1, class It2>
void SetBase::assign_union(It1 first, It2 last)
{
std::vector<Mixed> the_diff;
std::set_difference(first, last, begin(), end(), std::back_inserter(the_diff), SetElementLessThan<Mixed>{});
// 'the_diff' now contains all the elements that are in foreign set, but not in 'this'
// Now insert those elements
for (auto&& value : the_diff) {
insert_any(value);
}
}

void SetBase::assign_intersection(const CollectionBase& rhs)
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return assign_intersection(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return assign_intersection(other_set.begin(), other_set.end());
}

template <class It1, class It2>
void SetBase::assign_intersection(It1 first, It2 last)
{
std::vector<Mixed> intersection;
std::set_intersection(first, last, begin(), end(), std::back_inserter(intersection), SetElementLessThan<Mixed>{});
clear();
// Elements in intersection comes from foreign set, so ok to use here
for (auto&& value : intersection) {
insert_any(value);
}
}

void SetBase::assign_difference(const CollectionBase& rhs)
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return assign_difference(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return assign_difference(other_set.begin(), other_set.end());
}

template <class It1, class It2>
void SetBase::assign_difference(It1 first, It2 last)
{
std::vector<Mixed> intersection;
std::set_intersection(first, last, begin(), end(), std::back_inserter(intersection), SetElementLessThan<Mixed>{});
// 'intersection' now contains all the elements that are in both foreign set and 'this'.
// Remove those elements. The elements comes from the foreign set, so ok to refer to.
for (auto&& value : intersection) {
erase_any(value);
}
}

void SetBase::assign_symmetric_difference(const CollectionBase& rhs)
{
if (auto other_set = dynamic_cast<const SetBase*>(&rhs)) {
return assign_symmetric_difference(other_set->begin(), other_set->end());
}
auto other_set = convert_to_set(rhs);
return assign_symmetric_difference(other_set.begin(), other_set.end());
}

template <class It1, class It2>
void SetBase::assign_symmetric_difference(It1 first, It2 last)
{
std::vector<Mixed> difference;
std::set_difference(first, last, begin(), end(), std::back_inserter(difference), SetElementLessThan<Mixed>{});
std::vector<Mixed> intersection;
std::set_intersection(first, last, begin(), end(), std::back_inserter(intersection), SetElementLessThan<Mixed>{});
// Now remove the common elements and add the differences
for (auto&& value : intersection) {
erase_any(value);
}
for (auto&& value : difference) {
insert_any(value);
}
}

template <>
void Set<ObjKey>::do_insert(size_t ndx, ObjKey target_key)
{
Expand Down Expand Up @@ -357,36 +526,6 @@ void LnkSet::remove_all_target_rows()
}
}

bool LnkSet::is_subset_of(const CollectionBase& rhs) const
{
return this->m_set.is_subset_of(rhs);
}

bool LnkSet::is_strict_subset_of(const CollectionBase& rhs) const
{
return this->m_set.is_strict_subset_of(rhs);
}

bool LnkSet::is_superset_of(const CollectionBase& rhs) const
{
return this->m_set.is_superset_of(rhs);
}

bool LnkSet::is_strict_superset_of(const CollectionBase& rhs) const
{
return this->m_set.is_strict_superset_of(rhs);
}

bool LnkSet::intersects(const CollectionBase& rhs) const
{
return this->m_set.intersects(rhs);
}

bool LnkSet::set_equals(const CollectionBase& rhs) const
{
return this->m_set.set_equals(rhs);
}

void set_sorted_indices(size_t sz, std::vector<size_t>& indices, bool ascending)
{
indices.resize(sz);
Expand Down
Loading

0 comments on commit 2084261

Please sign in to comment.