Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]
### Added
- Added try_emplace(key, value) and try_emplace(iter_hint, key, value)
[#40](https://github.com/tzaeschke/phtree-cpp/issues/40)
- Added FilterBoxAABB and FilterSphereAABB as examples for filtering a PH-Tree with box keys
[#33](https://github.com/tzaeschke/phtree-cpp/issues/33)
### Changed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ PhPointD<3> p{1.1, 1.0, 10.};
// Some operations
tree.emplace(p, my_data);
tree.emplace_hint(hint, p, my_data);
tree.try_emplace(p, my_data);
tree.try_emplace(hint, p, my_data);
tree.insert(p, my_data);
tree[p] = my_data;
tree.count(p);
Expand Down
20 changes: 18 additions & 2 deletions phtree/phtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class PhTree {
*/
template <typename... Args>
std::pair<T&, bool> emplace(const Key& key, Args&&... args) {
return tree_.emplace(converter_.pre(key), std::forward<Args>(args)...);
return tree_.try_emplace(converter_.pre(key), std::forward<Args>(args)...);
}

/*
Expand All @@ -89,7 +89,7 @@ class PhTree {
*/
template <typename ITERATOR, typename... Args>
std::pair<T&, bool> emplace_hint(const ITERATOR& iterator, const Key& key, Args&&... args) {
return tree_.emplace_hint(iterator, converter_.pre(key), std::forward<Args>(args)...);
return tree_.try_emplace(iterator, converter_.pre(key), std::forward<Args>(args)...);
}

/*
Expand All @@ -102,6 +102,22 @@ class PhTree {
return tree_.insert(converter_.pre(key), value);
}

/*
* See emplace().
*/
template <typename... Args>
std::pair<T&, bool> try_emplace(const Key& key, Args&&... args) {
return tree_.try_emplace(converter_.pre(key), std::forward<Args>(args)...);
}

/*
* See emplace_hint().
*/
template <typename ITERATOR, typename... Args>
std::pair<T&, bool> try_emplace(const ITERATOR& iterator, const Key& key, Args&&... args) {
return tree_.try_emplace(iterator, converter_.pre(key), std::forward<Args>(args)...);
}

/*
* @return the value stored at position 'key'. If no such value exists, one is added to the tree
* and returned.
Expand Down
22 changes: 19 additions & 3 deletions phtree/phtree_multimap.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ class PhTreeMultiMap {
*/
template <typename... Args>
std::pair<T&, bool> emplace(const Key& key, Args&&... args) {
auto& outer_iter = tree_.emplace(converter_.pre(key)).first;
auto& outer_iter = tree_.try_emplace(converter_.pre(key)).first;
auto bucket_iter = outer_iter.emplace(std::forward<Args>(args)...);
size_ += bucket_iter.second ? 1 : 0;
return {const_cast<T&>(*bucket_iter.first), bucket_iter.second};
Expand All @@ -252,7 +252,7 @@ class PhTreeMultiMap {
*/
template <typename ITERATOR, typename... Args>
std::pair<T&, bool> emplace_hint(const ITERATOR& iterator, const Key& key, Args&&... args) {
auto result_ph = tree_.emplace_hint(iterator.GetIteratorOfPhTree(), converter_.pre(key));
auto result_ph = tree_.try_emplace(iterator.GetIteratorOfPhTree(), converter_.pre(key));
auto& bucket = result_ph.first;
if (result_ph.second) {
// new bucket
Expand Down Expand Up @@ -281,6 +281,22 @@ class PhTreeMultiMap {
return emplace(key, value);
}

/*
* See emplace().
*/
template <typename... Args>
std::pair<T&, bool> try_emplace(const Key& key, Args&&... args) {
return emplace(key, std::forward<Args>(args)...);
}

/*
* See emplace_hint().
*/
template <typename ITERATOR, typename... Args>
std::pair<T&, bool> try_emplace(const ITERATOR& iterator, const Key& key, Args&&... args) {
return emplace_hint(iterator, key, std::forward<Args>(args)...);
}

/*
* @return '1', if a value is associated with the provided key, otherwise '0'.
*/
Expand Down Expand Up @@ -405,7 +421,7 @@ class PhTreeMultiMap {
const Key& old_key, const Key& new_key, const T& value, bool always_erase = false) {
// Be smart: insert first, if the target-map already contains the entry we can avoid erase()
auto new_key_pre = converter_.pre(new_key);
auto& new_bucket = tree_.emplace(new_key_pre).first;
auto& new_bucket = tree_.try_emplace(new_key_pre).first;
auto new_result = new_bucket.emplace(value);
if (!new_result.second) {
// Entry is already in correct place -> abort
Expand Down
6 changes: 4 additions & 2 deletions phtree/phtree_multimap_d_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,12 @@ void SmokeTestBasicOps(size_t N) {
}

Id id(i);
if (i % 2 == 0) {
if (i % 4 == 0) {
ASSERT_TRUE(tree.emplace(p, id).second);
} else {
} else if (i % 4 == 0) {
ASSERT_TRUE(tree.insert(p, id).second);
} else{
ASSERT_TRUE(tree.try_emplace(p, id).second);
}
ASSERT_EQ(tree.count(p), i % NUM_DUPL + 1);
ASSERT_NE(tree.end(), tree.find(p));
Expand Down
39 changes: 37 additions & 2 deletions phtree/phtree_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,12 @@ void SmokeTestBasicOps(size_t N) {
ASSERT_EQ(tree.end(), tree.find(p));

Id id(i);
if (i % 2 == 0) {
if (i % 4 == 0) {
ASSERT_TRUE(tree.emplace(p, i).second);
} else {
} else if (i % 4 == 1) {
ASSERT_TRUE(tree.insert(p, id).second);
} else {
ASSERT_TRUE(tree.try_emplace(p, i).second);
}
ASSERT_EQ(tree.count(p), 1);
ASSERT_NE(tree.end(), tree.find(p));
Expand Down Expand Up @@ -567,6 +569,39 @@ TEST(PhTreeTest, TestUpdateWithEmplaceHint) {
ASSERT_EQ(2, tree.size());
}

TEST(PhTreeTest, TestUpdateWithTryEmplaceHint) {
const dimension_t dim = 3;
TestTree<dim, Id> tree;
size_t N = 10000;
std::array<int, 4> deltas{0, 1, 10, 100};
std::vector<TestPoint<dim>> points;
populate(tree, points, N);

size_t d_n = 0;
for (auto& p : points) {
auto pOld = p;
d_n = (d_n + 1) % deltas.size();
int delta = deltas[d_n];
TestPoint<dim> pNew{pOld[0] + delta, pOld[1] + delta, pOld[2] + delta};
auto iter = tree.find(pOld);
int n = tree.erase(iter);
ASSERT_EQ(1, n);
tree.try_emplace(iter, pNew, 42);
ASSERT_EQ(1, tree.count(pNew));
if (delta != 0.0) {
ASSERT_EQ(0, tree.count(pOld));
}
p = pNew;
}

ASSERT_EQ(N, tree.size());
tree.clear();

tree.try_emplace(tree.end(), {11, 21, 31}, 421);
tree.try_emplace(tree.begin(), {1, 2, 3}, 42);
ASSERT_EQ(2, tree.size());
}

TEST(PhTreeTest, TestEraseByIterator) {
const dimension_t dim = 3;
TestTree<dim, Id> tree;
Expand Down
22 changes: 11 additions & 11 deletions phtree/v16/phtree_v16.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class PhTreeV16 {
* entry instead of inserting a new one.
*/
template <typename... Args>
std::pair<T&, bool> emplace(const KeyT& key, Args&&... args) {
std::pair<T&, bool> try_emplace(const KeyT& key, Args&&... args) {
auto* current_entry = &root_;
bool is_inserted = false;
while (current_entry->IsNode()) {
Expand All @@ -104,7 +104,7 @@ class PhTreeV16 {
}

/*
* The emplace_hint() method uses an iterator as hint for insertion.
* The try_emplace(hint, key, value) method uses an iterator as hint for insertion.
* The hint is ignored if it is not useful or is equal to end().
*
* Iterators should normally not be used after the tree has been modified. As an exception to
Expand All @@ -116,33 +116,33 @@ class PhTreeV16 {
* auto iter = tree.find(key1);
* auto value = iter.second(); // The value may become invalid in erase()
* erase(iter);
* emplace_hint(iter, key2, value); // the iterator can still be used as hint here
* try_emplace(iter, key2, value); // the iterator can still be used as hint here
*/
template <typename ITERATOR, typename... Args>
std::pair<T&, bool> emplace_hint(const ITERATOR& iterator, const KeyT& key, Args&&... args) {
std::pair<T&, bool> try_emplace(const ITERATOR& iterator, const KeyT& key, Args&&... args) {
if constexpr (!std::is_same_v<ITERATOR, IteratorWithParent<T, CONVERT>>) {
return emplace(key, std::forward<Args>(args)...);
return try_emplace(key, std::forward<Args>(args)...);
} else {
// This function can be used to insert a value close to a known value
// or close to a recently removed value. The hint can only be used if the new key is
// inside one of the nodes provided by the hint iterator.
// The idea behind using the 'parent' is twofold:
// - The 'parent' node is one level above the iterator position, it is spatially
// larger and has a better probability of containing the new position, allowing for
// fast track emplace.
// fast track try_emplace.
// - Using 'parent' allows a scenario where the iterator was previously used with
// erase(iterator). This is safe because erase() will never erase the 'parent' node.

if (!iterator.GetParentNodeEntry()) {
// No hint available, use standard emplace()
return emplace(key, std::forward<Args>(args)...);
// No hint available, use standard try_emplace()
return try_emplace(key, std::forward<Args>(args)...);
}

auto* parent_entry = iterator.GetParentNodeEntry();
if (NumberOfDivergingBits(key, parent_entry->GetKey()) >
parent_entry->GetNodePostfixLen() + 1) {
// replace higher up in the tree
return emplace(key, std::forward<Args>(args)...);
return try_emplace(key, std::forward<Args>(args)...);
}

// replace in node
Expand All @@ -167,15 +167,15 @@ class PhTreeV16 {
* insertion) and a bool denoting whether the insertion took place.
*/
std::pair<T&, bool> insert(const KeyT& key, const T& value) {
return emplace(key, value);
return try_emplace(key, value);
}

/*
* @return the value stored at position 'key'. If no such value exists, one is added to the tree
* and returned.
*/
T& operator[](const KeyT& key) {
return emplace(key).first;
return try_emplace(key).first;
}

/*
Expand Down