Skip to content

Commit

Permalink
Add for_each_prefix_of methods to obtain path to longest prefix (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecorm committed May 21, 2023
1 parent e9fec92 commit 906e6ab
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 0 deletions.
72 changes: 72 additions & 0 deletions include/tsl/htrie_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,23 @@ class htrie_hash {
return longest_prefix_impl(*m_root, key, key_size);
}

template <class F>
void for_each_prefix_of(const CharT* key, size_type key_size, F&& visitor) {
if (m_root != nullptr) {
for_each_prefix_of_impl<iterator>(*m_root, key, key_size,
std::forward<F>(visitor));
}
}

template <class F>
void for_each_prefix_of(const CharT* key, size_type key_size,
F&& visitor) const {
if (m_root != nullptr) {
for_each_prefix_of_impl<const_iterator>(*m_root, key, key_size,
std::forward<F>(visitor));
}
}

/*
* Hash policy
*/
Expand Down Expand Up @@ -1600,6 +1617,61 @@ class htrie_hash {
return longest_found_prefix;
}

template <class Iterator, class N, class F>
void for_each_prefix_of_impl(N& search_start_node,
const CharT* value, size_type value_size,
F&& visitor) const {
auto* current_node = &search_start_node;

for (size_type ivalue = 0; ivalue < value_size; ivalue++) {
if (current_node->is_trie_node()) {
auto& tnode = current_node->as_trie_node();

if (tnode.val_node() != nullptr) {
visitor(Iterator(tnode));
}

if (tnode.child(value[ivalue]) == nullptr) {
return;
} else {
current_node = tnode.child(value[ivalue]).get();
}
} else {
auto& hnode = current_node->as_hash_node();

/**
* Test the presence in the hash node of each substring from the
* remaining [ivalue, value_size) string starting from the shortest.
* Also test the empty string.
*/
for (std::size_t i = value_size + 1; i > ivalue; i--) {
auto it =
hnode.array_hash().find_ks(value + ivalue, (value_size - i + 1));
if (it != hnode.array_hash().end()) {
visitor(Iterator(hnode, it));
}
}

return;
}
}

if (current_node->is_trie_node()) {
auto& tnode = current_node->as_trie_node();

if (tnode.val_node() != nullptr) {
visitor(Iterator(tnode));
}
} else {
auto& hnode = current_node->as_hash_node();

auto it = hnode.array_hash().find_ks("", 0);
if (it != hnode.array_hash().end()) {
visitor(Iterator(hnode, it));
}
}
}

std::pair<prefix_iterator, prefix_iterator> equal_prefix_range_impl(
anode& search_start_node, const CharT* prefix, size_type prefix_size) {
auto range = static_cast<const htrie_hash*>(this)->equal_prefix_range_impl(
Expand Down
94 changes: 94 additions & 0 deletions include/tsl/htrie_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,100 @@ class htrie_map {
}
#endif

/**
* Invoke the given `visitor` function for each element in the trie which is
* a prefix of `key`.
*
* @tparam F Callable target taking a single `iterator` or `const_iterator`
* argument.
*
* Example:
*
* tsl::htrie_map<char, int> map = {{"/foo", 1}, {"/foo/bar", 2}};
* auto print = [](tsl::htrie_map<char>::iterator it) {
* std::cout << it.key() << "\n";
* };
*
* map.for_each_prefix_of("/foo", print); // prints "/foo"
* map.for_each_prefix_of("/foo/baz"); // prints nothing
* map.for_each_prefix_of("/foo/bar/baz"); // prints nothing
* map.for_each_prefix_of("/foo/bar/"); // prints "/foo" and "/foo/bar"
* map.for_each_prefix_of("/bar"); // prints nothing
* map.for_each_prefix_of(""); // prints nothing
*/
template <typename F>
void for_each_prefix_of_ks(const CharT* key, size_type key_size,
F&& visitor) {
return m_ht.for_each_prefix_of(key, key_size, std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of_ks(const CharT* key, size_type key_size,
F&& visitor) const {
return m_ht.for_each_prefix_of(key, key_size, std::forward<F>(visitor));
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const std::basic_string_view<CharT>& key,
F&& visitor) {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(
const std::basic_string_view<CharT>& key, F&& visitor) const {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}
#else
/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const CharT* key, F&& visitor) {
return m_ht.for_each_prefix_of(key, std::strlen(key),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const CharT* key, F&& visitor) const {
return m_ht.for_each_prefix_of(key, std::strlen(key),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const std::basic_string<CharT>& key, F&& visitor) {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const std::basic_string<CharT>& key,
F&& visitor) const {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}
#endif

/*
* Hash policy
*/
Expand Down
94 changes: 94 additions & 0 deletions include/tsl/htrie_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,100 @@ class htrie_set {
}
#endif

/**
* Invoke the given `visitor` function for each element in the trie which is
* a prefix of `key`.
*
* @tparam F Callable target taking a single `iterator` or `const_iterator`
* argument.
*
* Example:
*
* tsl::htrie_set<char> set = {"/foo", "/foo/bar"};
* auto print = [](tsl::htrie_set<char>::iterator it) {
* std::cout << it.key() << "\n";
* };
*
* set.for_each_prefix_of("/foo", print); // prints "/foo"
* set.for_each_prefix_of("/foo/baz"); // prints nothing
* set.for_each_prefix_of("/foo/bar/baz"); // prints nothing
* set.for_each_prefix_of("/foo/bar/"); // prints "/foo" and "/foo/bar"
* set.for_each_prefix_of("/bar"); // prints nothing
* set.for_each_prefix_of(""); // prints nothing
*/
template <typename F>
void for_each_prefix_of_ks(const CharT* key, size_type key_size,
F&& visitor) {
return m_ht.for_each_prefix_of(key, key_size, std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of_ks(const CharT* key, size_type key_size,
F&& visitor) const {
return m_ht.for_each_prefix_of(key, key_size, std::forward<F>(visitor));
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const std::basic_string_view<CharT>& key,
F&& visitor) {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(
const std::basic_string_view<CharT>& key, F&& visitor) const {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}
#else
/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const CharT* key, F&& visitor) {
return m_ht.for_each_prefix_of(key, std::strlen(key),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const CharT* key, F&& visitor) const {
return m_ht.for_each_prefix_of(key, std::strlen(key),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const std::basic_string<CharT>& key, F&& visitor) {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}

/**
* @copydoc for_each_prefix_of_ks(const CharT*, size_type, F&&)
*/
template <typename F>
void for_each_prefix_of(const std::basic_string<CharT>& key,
F&& visitor) const {
return m_ht.for_each_prefix_of(key.data(), key.size(),
std::forward<F>(visitor));
}
#endif

/*
* Hash policy
*/
Expand Down
43 changes: 43 additions & 0 deletions tests/trie_map_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,49 @@ BOOST_AUTO_TEST_CASE(test_longest_prefix) {
BOOST_CHECK_EQUAL(map.longest_prefix("").key(), "");
}

/**
* for_each_prefix_of
*/
BOOST_AUTO_TEST_CASE(test_for_each_prefix_of) {
using map_type = tsl::htrie_map<char, int>;
using path_type = std::vector<std::string>;

map_type map(4);
map = {{"a", 1}, {"aa", 1}, {"aaa", 1}, {"aaaaa", 1},
{"aaaaaa", 1}, {"aaaaaaa", 1}, {"ab", 1}, {"abcde", 1},
{"abcdf", 1}, {"abcdg", 1}, {"abcdh", 1}, {"babc", 1}};

std::vector<std::pair<const char*, path_type>> test_vectors = {
{"a", {"a"}},
{"aa", {"a", "aa"}},
{"aaa", {"a", "aa", "aaa"}},
{"aaaa", {"a", "aa", "aaa"}},
{"ab", {"a", "ab"}},
{"abc", {"a", "ab"}},
{"abcd", {"a", "ab"}},
{"abcdz", {"a", "ab"}},
{"abcde", {"a", "ab", "abcde"}},
{"abcdef", {"a", "ab", "abcde"}},
{"abcdefg", {"a", "ab", "abcde"}},
{"dabc", {}},
{"b", {}},
{"bab", {}},
{"babd", {}},
{"", {}},
};

for (const auto& v: test_vectors) {
path_type p;
const path_type& expected = v.second;
auto visitor = [&p](map_type::const_iterator it) {p.push_back(it.key());};
map.for_each_prefix_of(v.first, visitor);
BOOST_CHECK_EQUAL_COLLECTIONS(p.begin(), p.end(),
expected.begin(), expected.end());
if (p != expected)
BOOST_TEST_MESSAGE("...for test vector input '" << v.first << "'");
}
}

/**
* erase_prefix
*/
Expand Down

0 comments on commit 906e6ab

Please sign in to comment.