183 lines
6.7 KiB
C++
183 lines
6.7 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2016 Realm Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef REALM_COLLECTION_NOTIFICATIONS_HPP
|
|
#define REALM_COLLECTION_NOTIFICATIONS_HPP
|
|
|
|
#include "index_set.hpp"
|
|
#include "util/atomic_shared_ptr.hpp"
|
|
|
|
#include <exception>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
namespace realm {
|
|
namespace _impl {
|
|
class CollectionNotifier;
|
|
}
|
|
|
|
// A token which keeps an asynchronous query alive
|
|
struct NotificationToken {
|
|
NotificationToken() = default;
|
|
NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, uint64_t token);
|
|
~NotificationToken();
|
|
|
|
NotificationToken(NotificationToken&&);
|
|
NotificationToken& operator=(NotificationToken&&);
|
|
|
|
NotificationToken(NotificationToken const&) = delete;
|
|
NotificationToken& operator=(NotificationToken const&) = delete;
|
|
|
|
void suppress_next();
|
|
|
|
private:
|
|
util::AtomicSharedPtr<_impl::CollectionNotifier> m_notifier;
|
|
uint64_t m_token;
|
|
};
|
|
|
|
struct CollectionChangeSet {
|
|
struct Move {
|
|
size_t from;
|
|
size_t to;
|
|
|
|
bool operator==(Move m) const noexcept { return from == m.from && to == m.to; }
|
|
};
|
|
|
|
// Indices which were removed from the _old_ collection
|
|
IndexSet deletions;
|
|
|
|
// Indices in the _new_ collection which are new insertions
|
|
IndexSet insertions;
|
|
|
|
// Indices of objects in the _old_ collection which were modified
|
|
IndexSet modifications;
|
|
|
|
// Indices in the _new_ collection which were modified. This will always
|
|
// have the same number of indices as `modifications` and conceptually
|
|
// represents the same entries, just in different versions of the collection.
|
|
// It exists for the sake of code which finds it easier to process
|
|
// modifications after processing deletions and insertions rather than before.
|
|
IndexSet modifications_new;
|
|
|
|
// Rows in the collection which moved.
|
|
//
|
|
// Every `from` index will also be present in `deletions` and every `to`
|
|
// index will be present in `insertions`.
|
|
//
|
|
// This is currently not reliably calculated for all types of collections. A
|
|
// reported move will always actually be a move, but there may also be
|
|
// unreported moves which show up only as a delete/insert pair.
|
|
std::vector<Move> moves;
|
|
|
|
// Per-column version of `modifications`
|
|
std::vector<IndexSet> columns;
|
|
|
|
bool empty() const noexcept
|
|
{
|
|
return deletions.empty() && insertions.empty() && modifications.empty()
|
|
&& modifications_new.empty() && moves.empty();
|
|
}
|
|
};
|
|
|
|
// A type-erasing wrapper for the callback for collection notifications. Can be
|
|
// constructed with either any callable compatible with the signature
|
|
// `void (CollectionChangeSet, std::exception_ptr)`, an object with member
|
|
// functions `void before(CollectionChangeSet)`, `void after(CollectionChangeSet)`,
|
|
// `void error(std::exception_ptr)`, or a pointer to such an object. If a pointer
|
|
// is given, the caller is responsible for ensuring that the pointed-to object
|
|
// outlives the collection.
|
|
class CollectionChangeCallback {
|
|
public:
|
|
CollectionChangeCallback(std::nullptr_t={}) { }
|
|
|
|
template<typename Callback>
|
|
CollectionChangeCallback(Callback cb) : m_impl(make_impl(std::move(cb))) { }
|
|
template<typename Callback>
|
|
CollectionChangeCallback& operator=(Callback cb) { m_impl = make_impl(std::move(cb)); return *this; }
|
|
|
|
// Explicitly default the copy/move constructors as otherwise they'll use
|
|
// the above ones and add an extra layer of wrapping
|
|
CollectionChangeCallback(CollectionChangeCallback&&) = default;
|
|
CollectionChangeCallback(CollectionChangeCallback const&) = default;
|
|
CollectionChangeCallback& operator=(CollectionChangeCallback&&) = default;
|
|
CollectionChangeCallback& operator=(CollectionChangeCallback const&) = default;
|
|
|
|
void before(CollectionChangeSet const& c) { m_impl->before(c); }
|
|
void after(CollectionChangeSet const& c) { m_impl->after(c); }
|
|
void error(std::exception_ptr e) { m_impl->error(e); }
|
|
|
|
explicit operator bool() const { return !!m_impl; }
|
|
|
|
private:
|
|
struct Base {
|
|
virtual ~Base() {}
|
|
virtual void before(CollectionChangeSet const&)=0;
|
|
virtual void after(CollectionChangeSet const&)=0;
|
|
virtual void error(std::exception_ptr)=0;
|
|
};
|
|
|
|
template<typename Callback, typename = decltype(std::declval<Callback>()(CollectionChangeSet(), std::exception_ptr()))>
|
|
std::shared_ptr<Base> make_impl(Callback cb)
|
|
{
|
|
return std::make_shared<Impl<Callback>>(std::move(cb));
|
|
}
|
|
|
|
template<typename Callback, typename = decltype(std::declval<Callback>().after(CollectionChangeSet())), typename = void>
|
|
std::shared_ptr<Base> make_impl(Callback cb)
|
|
{
|
|
return std::make_shared<Impl2<Callback>>(std::move(cb));
|
|
}
|
|
|
|
template<typename Callback, typename = decltype(std::declval<Callback>().after(CollectionChangeSet())), typename = void>
|
|
std::shared_ptr<Base> make_impl(Callback* cb)
|
|
{
|
|
return std::make_shared<Impl3<Callback>>(cb);
|
|
}
|
|
|
|
template<typename T>
|
|
struct Impl : public Base {
|
|
T impl;
|
|
Impl(T impl) : impl(std::move(impl)) { }
|
|
void before(CollectionChangeSet const&) override { }
|
|
void after(CollectionChangeSet const& change) override { impl(change, {}); }
|
|
void error(std::exception_ptr error) override { impl({}, error); }
|
|
};
|
|
template<typename T>
|
|
struct Impl2 : public Base {
|
|
T impl;
|
|
Impl2(T impl) : impl(std::move(impl)) { }
|
|
void before(CollectionChangeSet const& c) override { impl.before(c); }
|
|
void after(CollectionChangeSet const& c) override { impl.after(c); }
|
|
void error(std::exception_ptr error) override { impl.error(error); }
|
|
};
|
|
template<typename T>
|
|
struct Impl3 : public Base {
|
|
T* impl;
|
|
Impl3(T* impl) : impl(impl) { }
|
|
void before(CollectionChangeSet const& c) override { impl->before(c); }
|
|
void after(CollectionChangeSet const& c) override { impl->after(c); }
|
|
void error(std::exception_ptr error) override { impl->error(error); }
|
|
};
|
|
|
|
std::shared_ptr<Base> m_impl;
|
|
};
|
|
} // namespace realm
|
|
|
|
#endif // REALM_COLLECTION_NOTIFICATIONS_HPP
|