Instead of lengthy blurbs, switch to single-line, machine-readable standardized (https://spdx.dev) license identifiers. The Linux kernel switched long ago, so there is strong precedent. Three cases are handled: AGPL-only, Apache-only, and dual licensed. For the latter case, I chose (AGPL-3.0-or-later and Apache-2.0), reasoning that our changes are extensive enough to apply our license. The changes we applied mechanically with a script, except to licenses/README.md. Closes #9937
285 lines
13 KiB
C++
285 lines
13 KiB
C++
/*
|
|
* Copyright (C) 2016-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
/*
|
|
* (C) Copyright Ion Gaztanaga 2013-2014
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <seastar/util/defer.hh>
|
|
#include <boost/intrusive/set.hpp>
|
|
#include <iterator>
|
|
#include <boost/intrusive/parent_from_member.hpp>
|
|
|
|
namespace bi = boost::intrusive;
|
|
|
|
typedef bi::rbtree_algorithms<bi::rbtree_node_traits<void*, true>> algo;
|
|
|
|
class intrusive_set_external_comparator_member_hook : public bi::set_member_hook<bi::optimize_size<true>, bi::link_mode<bi::auto_unlink>> {
|
|
public:
|
|
intrusive_set_external_comparator_member_hook() = default;
|
|
intrusive_set_external_comparator_member_hook(intrusive_set_external_comparator_member_hook&& o) noexcept {
|
|
algo::replace_node(o.this_ptr(), this_ptr());
|
|
algo::init(o.this_ptr());
|
|
}
|
|
};
|
|
|
|
template<typename Elem,
|
|
intrusive_set_external_comparator_member_hook Elem::* PtrToMember>
|
|
class intrusive_set_external_comparator final {
|
|
typedef boost::intrusive::mhtraits<Elem, intrusive_set_external_comparator_member_hook, PtrToMember> value_traits;
|
|
typedef typename value_traits::node_traits node_traits;
|
|
typedef typename node_traits::node_ptr node_ptr;
|
|
|
|
using boost_iterator = typename bi::tree_iterator<value_traits, false>;
|
|
using boost_const_iterator = typename bi::tree_iterator<value_traits, true>;
|
|
public:
|
|
typedef Elem value_type;
|
|
|
|
struct iterator : boost_iterator {
|
|
using boost_iterator::boost_iterator;
|
|
iterator(boost_iterator it) noexcept : boost_iterator(it) { }
|
|
iterator(const iterator& other) = default;
|
|
iterator(iterator&& other) noexcept : boost_iterator(other) { }
|
|
iterator& operator=(const iterator& other) = default;
|
|
iterator& operator=(iterator&& other) noexcept {
|
|
return *this = other;
|
|
}
|
|
iterator& operator++() {
|
|
boost_iterator::operator++();
|
|
return *this;
|
|
}
|
|
iterator operator++(int) {
|
|
return iterator(boost_iterator::operator++(int()));
|
|
}
|
|
iterator unconst() const {
|
|
return *this;
|
|
}
|
|
};
|
|
struct const_iterator : boost_const_iterator {
|
|
using boost_const_iterator::boost_const_iterator;
|
|
const_iterator(boost_const_iterator it) noexcept : boost_const_iterator(it) { }
|
|
const_iterator(const const_iterator& other) = default;
|
|
const_iterator(const iterator& other) : boost_const_iterator(boost_iterator(other)) { }
|
|
const_iterator(const_iterator&& other) noexcept : boost_const_iterator(other) { }
|
|
const_iterator& operator=(const const_iterator& other) = default;
|
|
const_iterator& operator=(const_iterator&& other) noexcept {
|
|
return *this = other;
|
|
}
|
|
const_iterator& operator++() {
|
|
boost_const_iterator::operator++();
|
|
return *this;
|
|
}
|
|
const_iterator operator++(int) {
|
|
return const_iterator(boost_const_iterator::operator++(int()));
|
|
}
|
|
iterator unconst() const {
|
|
return iterator(boost_const_iterator::unconst());
|
|
}
|
|
};
|
|
|
|
typedef typename std::reverse_iterator<iterator> reverse_iterator;
|
|
typedef typename std::reverse_iterator<const_iterator> const_reverse_iterator;
|
|
|
|
private:
|
|
intrusive_set_external_comparator_member_hook _header;
|
|
static const value_traits _value_traits;
|
|
|
|
struct key_of_value {
|
|
typedef Elem type;
|
|
Elem& operator()(Elem& t) { return t; }
|
|
};
|
|
|
|
template <typename Comparator>
|
|
struct key_node_comparator {
|
|
Comparator _cmp;
|
|
const value_traits& _value_traits;
|
|
key_node_comparator(Comparator cmp, const value_traits& value_traits) : _cmp(cmp), _value_traits(value_traits) { }
|
|
bool operator()(const node_ptr& a, const node_ptr& b) {
|
|
return _cmp(*_value_traits.to_value_ptr(a), *_value_traits.to_value_ptr(b));
|
|
}
|
|
template <typename T1>
|
|
bool operator()(const node_ptr& a, const T1& b) {
|
|
return _cmp(*_value_traits.to_value_ptr(a), b);
|
|
}
|
|
template <typename T1>
|
|
bool operator()(const T1& a, const node_ptr& b) {
|
|
return _cmp(a, *_value_traits.to_value_ptr(b));
|
|
}
|
|
template <typename T1, typename T2>
|
|
bool operator()(const T1& a, const T2& b) {
|
|
return _cmp(a, b);
|
|
}
|
|
};
|
|
|
|
using const_value_traits_ptr = typename std::pointer_traits<typename value_traits::node_ptr>::template rebind<const value_traits>;
|
|
|
|
static const_value_traits_ptr priv_value_traits_ptr() {
|
|
return bi::pointer_traits<const_value_traits_ptr>::pointer_to(_value_traits);
|
|
}
|
|
template <typename Comparator>
|
|
key_node_comparator<Comparator> key_node_comp(Comparator comp) const {
|
|
return key_node_comparator<Comparator>(comp, _value_traits);
|
|
}
|
|
iterator insert_unique_commit(Elem& value, const algo::insert_commit_data &commit_data) {
|
|
node_ptr to_insert(_value_traits.to_node_ptr(value));
|
|
algo::insert_unique_commit(_header.this_ptr(), to_insert, commit_data);
|
|
return iterator(to_insert, priv_value_traits_ptr());
|
|
}
|
|
public:
|
|
intrusive_set_external_comparator() { algo::init_header(_header.this_ptr()); }
|
|
intrusive_set_external_comparator(intrusive_set_external_comparator&& o) noexcept {
|
|
algo::init_header(_header.this_ptr());
|
|
algo::swap_tree(_header.this_ptr(), node_ptr(o._header.this_ptr()));
|
|
}
|
|
static iterator iterator_to(Elem& e) { return iterator(_value_traits.to_node_ptr(e), priv_value_traits_ptr()); }
|
|
// Returns container of e, assuming is_only_member(e).
|
|
static intrusive_set_external_comparator& container_of_only_member(Elem& e) {
|
|
auto header_ptr = static_cast<intrusive_set_external_comparator_member_hook*>(
|
|
algo::node_traits::get_parent(_value_traits.to_node_ptr(e)));
|
|
return *boost::intrusive::get_parent_from_member(header_ptr, &intrusive_set_external_comparator::_header);
|
|
}
|
|
static bool is_root(Elem& e) {
|
|
auto node = _value_traits.to_node_ptr(e);
|
|
auto e_parent = algo::node_traits::get_parent(node);
|
|
return algo::node_traits::get_parent(e_parent) == node;
|
|
}
|
|
// Returns true if and only if e is the only member of the tree.
|
|
static bool is_only_member(Elem& e) {
|
|
auto node = _value_traits.to_node_ptr(e);
|
|
return is_root(e) && !algo::node_traits::get_left(node) && !algo::node_traits::get_right(node);
|
|
}
|
|
iterator begin() { return iterator(algo::begin_node(_header.this_ptr()), priv_value_traits_ptr()); }
|
|
const_iterator begin() const { return const_iterator(algo::begin_node(_header.this_ptr()), priv_value_traits_ptr()); }
|
|
iterator end() { return iterator(algo::end_node(_header.this_ptr()), priv_value_traits_ptr()); }
|
|
const_iterator end() const { return const_iterator(algo::end_node(_header.this_ptr()), priv_value_traits_ptr()); }
|
|
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
|
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
|
reverse_iterator rend() { return reverse_iterator(begin()); }
|
|
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
|
template<class Disposer>
|
|
void clear_and_dispose(Disposer disposer) {
|
|
algo::clear_and_dispose(_header.this_ptr(),
|
|
[&disposer] (const node_ptr& p) {
|
|
disposer(_value_traits.to_value_ptr(p));
|
|
});
|
|
algo::init_header(_header.this_ptr());
|
|
}
|
|
bool empty() const { return algo::unique(_header.this_ptr()); }
|
|
|
|
// WARNING: this method has O(N) time complexity, use with care
|
|
auto calculate_size() const { return algo::size(_header.this_ptr()); }
|
|
iterator erase(const_iterator i) {
|
|
const_iterator ret(i);
|
|
++ret;
|
|
node_ptr to_erase(i.pointed_node());
|
|
algo::erase(_header.this_ptr(), to_erase);
|
|
algo::init(to_erase);
|
|
return ret.unconst();
|
|
}
|
|
iterator erase(const_iterator b, const_iterator e) {
|
|
while (b != e) {
|
|
erase(b++);
|
|
}
|
|
return b.unconst();
|
|
}
|
|
template<class Disposer>
|
|
iterator erase_and_dispose(const_iterator i, Disposer disposer) {
|
|
node_ptr to_erase(i.pointed_node());
|
|
iterator ret(erase(i));
|
|
disposer(_value_traits.to_value_ptr(to_erase));
|
|
return ret;
|
|
}
|
|
template<class Disposer>
|
|
iterator erase_and_dispose(const_iterator b, const_iterator e, Disposer disposer) {
|
|
while (b != e) {
|
|
erase_and_dispose(b++, disposer);
|
|
}
|
|
return b.unconst();
|
|
}
|
|
template <class Cloner, class Disposer>
|
|
void clone_from(const intrusive_set_external_comparator &src, Cloner cloner, Disposer disposer) {
|
|
clear_and_dispose(disposer);
|
|
if (!src.empty()) {
|
|
auto rollback = defer([this, &disposer] () noexcept {
|
|
// terminate if clear_and_dispose throws
|
|
// since we cannot recover from that.
|
|
this->clear_and_dispose(disposer);
|
|
});
|
|
algo::clone(src._header.this_ptr(),
|
|
_header.this_ptr(),
|
|
[&cloner] (const node_ptr& p) {
|
|
return _value_traits.to_node_ptr(*cloner(*_value_traits.to_value_ptr(p)));
|
|
},
|
|
[&disposer] (const node_ptr& p) {
|
|
disposer(_value_traits.to_value_ptr(p));
|
|
});
|
|
rollback.cancel();
|
|
}
|
|
}
|
|
Elem* unlink_leftmost_without_rebalance() {
|
|
node_ptr to_be_disposed(algo::unlink_leftmost_without_rebalance(_header.this_ptr()));
|
|
if(!to_be_disposed)
|
|
return 0;
|
|
algo::init(to_be_disposed);
|
|
return _value_traits.to_value_ptr(to_be_disposed);
|
|
}
|
|
iterator insert_before(const_iterator pos, Elem& value) {
|
|
node_ptr to_insert(_value_traits.to_node_ptr(value));
|
|
return iterator(algo::insert_before(_header.this_ptr(), pos.pointed_node(), to_insert), priv_value_traits_ptr());
|
|
}
|
|
template<class KeyType, class KeyTypeKeyCompare>
|
|
iterator upper_bound(const KeyType& key, KeyTypeKeyCompare comp) {
|
|
return iterator(algo::upper_bound(_header.this_ptr(), key, key_node_comp(comp)), priv_value_traits_ptr());
|
|
}
|
|
template<class KeyType, class KeyTypeKeyCompare>
|
|
const_iterator upper_bound(const KeyType& key, KeyTypeKeyCompare comp) const {
|
|
return const_iterator(algo::upper_bound(_header.this_ptr(), key, key_node_comp(comp)), priv_value_traits_ptr());
|
|
}
|
|
template<class KeyType, class KeyTypeKeyCompare>
|
|
iterator lower_bound(const KeyType &key, KeyTypeKeyCompare comp) {
|
|
return iterator(algo::lower_bound(_header.this_ptr(), key, key_node_comp(comp)), priv_value_traits_ptr());
|
|
}
|
|
template<class KeyType, class KeyTypeKeyCompare>
|
|
const_iterator lower_bound(const KeyType &key, KeyTypeKeyCompare comp) const {
|
|
return const_iterator(algo::lower_bound(_header.this_ptr(), key, key_node_comp(comp)), priv_value_traits_ptr());
|
|
}
|
|
template<class KeyType, class KeyTypeKeyCompare>
|
|
iterator find(const KeyType &key, KeyTypeKeyCompare comp) {
|
|
return iterator(algo::find(_header.this_ptr(), key, key_node_comp(comp)), priv_value_traits_ptr());
|
|
}
|
|
template<class KeyType, class KeyTypeKeyCompare>
|
|
const_iterator find(const KeyType &key, KeyTypeKeyCompare comp) const {
|
|
return const_iterator(algo::find(_header.this_ptr(), key, key_node_comp(comp)), priv_value_traits_ptr());
|
|
}
|
|
template<class ElemCompare>
|
|
iterator insert(const_iterator hint, Elem& value, ElemCompare cmp) {
|
|
return insert_check(hint, value, std::move(cmp)).first;
|
|
}
|
|
template<class ElemCompare>
|
|
std::pair<iterator, bool> insert_check(const_iterator hint, Elem& value, ElemCompare cmp) {
|
|
algo::insert_commit_data commit_data;
|
|
std::pair<node_ptr, bool> ret =
|
|
algo::insert_unique_check(_header.this_ptr(),
|
|
hint.pointed_node(),
|
|
key_of_value()(value),
|
|
key_node_comp(cmp),
|
|
commit_data);
|
|
return ret.second ? std::make_pair(insert_unique_commit(value, commit_data), true)
|
|
: std::make_pair(iterator(ret.first, priv_value_traits_ptr()), false);
|
|
}
|
|
};
|
|
|
|
template<typename Elem,
|
|
intrusive_set_external_comparator_member_hook Elem::* PtrToMember>
|
|
const typename intrusive_set_external_comparator<Elem, PtrToMember>::value_traits intrusive_set_external_comparator<Elem, PtrToMember>::_value_traits;
|