| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /** |
| * Copyright (c) 2011-2015 Regents of the University of California. |
| * |
| * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and |
| * contributors. |
| * |
| * ndnSIM is free software: you can redistribute it and/or modify it under the terms |
| * of the GNU General Public License as published by the Free Software Foundation, |
| * either version 3 of the License, or (at your option) any later version. |
| * |
| * ndnSIM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
| * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| * PURPOSE. See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * ndnSIM, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>. |
| **/ |
| |
| #ifndef TRIE_H_ |
| #define TRIE_H_ |
| |
| /// @cond include_hidden |
| |
| #include "ns3/ndnSIM/model/ndn-common.hpp" |
| |
| #include "ns3/ptr.h" |
| |
| #include <boost/intrusive/unordered_set.hpp> |
| #include <boost/intrusive/list.hpp> |
| #include <boost/intrusive/set.hpp> |
| #include <boost/functional/hash.hpp> |
| #include <boost/interprocess/smart_ptr/unique_ptr.hpp> |
| #include <tuple> |
| #include <boost/foreach.hpp> |
| #include <boost/mpl/if.hpp> |
| |
| namespace ns3 { |
| namespace ndn { |
| namespace ndnSIM { |
| |
| ///////////////////////////////////////////////////// |
| // Allow customization for payload |
| // |
| template<typename Payload, typename BasePayload = Payload> |
| struct pointer_payload_traits { |
| typedef Payload payload_type; // general type of the payload |
| typedef Payload* storage_type; // how the payload is actually stored |
| typedef Payload* insert_type; // what parameter is inserted |
| |
| typedef Payload* return_type; // what is returned on access |
| typedef const Payload* const_return_type; // what is returned on const access |
| |
| typedef BasePayload* |
| base_type; // base type of the entry (when implementation details need to be hidden) |
| typedef const BasePayload* |
| const_base_type; // const base type of the entry (when implementation details need to be hidden) |
| |
| static Payload* empty_payload; |
| }; |
| |
| template<typename Payload, typename BasePayload> |
| Payload* pointer_payload_traits<Payload, BasePayload>::empty_payload = 0; |
| |
| template<typename Payload, typename BasePayload = Payload> |
| struct smart_pointer_payload_traits { |
| typedef Payload payload_type; |
| typedef ns3::Ptr<Payload> storage_type; |
| typedef ns3::Ptr<Payload> insert_type; |
| |
| typedef ns3::Ptr<Payload> return_type; |
| typedef ns3::Ptr<const Payload> const_return_type; |
| |
| typedef ns3::Ptr<BasePayload> base_type; |
| typedef ns3::Ptr<const BasePayload> const_base_type; |
| |
| static ns3::Ptr<Payload> empty_payload; |
| }; |
| |
| template<typename Payload, typename BasePayload> |
| ns3::Ptr<Payload> smart_pointer_payload_traits<Payload, BasePayload>::empty_payload = 0; |
| |
| template<typename Payload, typename BasePayload = Payload> |
| struct non_pointer_traits { |
| typedef Payload payload_type; |
| typedef Payload storage_type; |
| typedef const Payload& insert_type; // nothing to insert |
| |
| typedef Payload& return_type; |
| typedef const Payload& const_return_type; |
| |
| typedef BasePayload& base_type; |
| typedef const BasePayload& const_base_type; |
| |
| static Payload empty_payload; |
| }; |
| |
| template<typename Payload, typename BasePayload> |
| Payload non_pointer_traits<Payload, BasePayload>::empty_payload = Payload(); |
| |
| //////////////////////////////////////////////////// |
| // forward declarations |
| // |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| class trie; |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| inline std::ostream& |
| operator<<(std::ostream& os, const trie<FullKey, PayloadTraits, PolicyHook>& trie_node); |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| bool |
| operator==(const trie<FullKey, PayloadTraits, PolicyHook>& a, |
| const trie<FullKey, PayloadTraits, PolicyHook>& b); |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| std::size_t |
| hash_value(const trie<FullKey, PayloadTraits, PolicyHook>& trie_node); |
| |
| /////////////////////////////////////////////////// |
| // actual definition |
| // |
| template<class T, class NonConstT> |
| class trie_iterator; |
| |
| template<class T> |
| class trie_point_iterator; |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| class trie { |
| public: |
| typedef typename FullKey::value_type Key; |
| |
| typedef trie* iterator; |
| typedef const trie* const_iterator; |
| |
| typedef trie_iterator<trie, trie> recursive_iterator; |
| typedef trie_iterator<const trie, trie> const_recursive_iterator; |
| |
| typedef trie_point_iterator<trie> point_iterator; |
| typedef trie_point_iterator<const trie> const_point_iterator; |
| |
| typedef PayloadTraits payload_traits; |
| |
| inline trie(const Key& key, size_t bucketSize = 1, size_t bucketIncrement = 1) |
| : key_(key) |
| , initialBucketSize_(bucketSize) |
| , bucketIncrement_(bucketIncrement) |
| , bucketSize_(initialBucketSize_) |
| , buckets_(new bucket_type[bucketSize_]) // cannot use normal pointer, because lifetime of |
| // buckets should be larger than lifetime of the |
| // container |
| , children_(bucket_traits(buckets_.get(), bucketSize_)) |
| , payload_(PayloadTraits::empty_payload) |
| , parent_(nullptr) |
| { |
| } |
| |
| inline ~trie() |
| { |
| payload_ = PayloadTraits::empty_payload; // necessary for smart pointers... |
| children_.clear_and_dispose(trie_delete_disposer()); |
| } |
| |
| void |
| clear() |
| { |
| children_.clear_and_dispose(trie_delete_disposer()); |
| } |
| |
| template<class Predicate> |
| void |
| clear_if(Predicate cond) |
| { |
| recursive_iterator trieNode(this); |
| recursive_iterator end(0); |
| |
| while (trieNode != end) { |
| if (cond(*trieNode)) { |
| trieNode = recursive_iterator(trieNode->erase()); |
| } |
| trieNode++; |
| } |
| } |
| |
| // actual entry |
| friend bool operator==<>(const trie<FullKey, PayloadTraits, PolicyHook>& a, |
| const trie<FullKey, PayloadTraits, PolicyHook>& b); |
| |
| friend std::size_t |
| hash_value<>(const trie<FullKey, PayloadTraits, PolicyHook>& trie_node); |
| |
| inline std::pair<iterator, bool> |
| insert(const FullKey& key, typename PayloadTraits::insert_type payload) |
| { |
| trie* trieNode = this; |
| |
| BOOST_FOREACH (const Key& subkey, key) { |
| typename unordered_set::iterator item = trieNode->children_.find(subkey); |
| if (item == trieNode->children_.end()) { |
| trie* newNode = new trie(subkey, initialBucketSize_, bucketIncrement_); |
| // std::cout << "new " << newNode << "\n"; |
| newNode->parent_ = trieNode; |
| |
| if (trieNode->children_.size() >= trieNode->bucketSize_) { |
| trieNode->bucketSize_ += trieNode->bucketIncrement_; |
| trieNode->bucketIncrement_ *= 2; // increase bucketIncrement exponentially |
| |
| buckets_array newBuckets(new bucket_type[trieNode->bucketSize_]); |
| trieNode->children_.rehash(bucket_traits(newBuckets.get(), trieNode->bucketSize_)); |
| trieNode->buckets_.swap(newBuckets); |
| } |
| |
| std::pair<typename unordered_set::iterator, bool> ret = |
| trieNode->children_.insert(*newNode); |
| |
| trieNode = &(*ret.first); |
| } |
| else |
| trieNode = &(*item); |
| } |
| |
| if (trieNode->payload_ == PayloadTraits::empty_payload) { |
| trieNode->payload_ = payload; |
| return std::make_pair(trieNode, true); |
| } |
| else |
| return std::make_pair(trieNode, false); |
| } |
| |
| /** |
| * @brief Removes payload (if it exists) and if there are no children, prunes parents trie |
| */ |
| inline iterator |
| erase() |
| { |
| payload_ = PayloadTraits::empty_payload; |
| return prune(); |
| } |
| |
| /** |
| * @brief Do exactly as erase, but without erasing the payload |
| */ |
| inline iterator |
| prune() |
| { |
| if (payload_ == PayloadTraits::empty_payload && children_.size() == 0) { |
| if (parent_ == 0) |
| return this; |
| |
| trie* parent = parent_; |
| parent->children_ |
| .erase_and_dispose(*this, |
| trie_delete_disposer()); // delete this; basically, committing a suicide |
| |
| return parent->prune(); |
| } |
| return this; |
| } |
| |
| /** |
| * @brief Perform prune of the node, but without attempting to parent of the node |
| */ |
| inline void |
| prune_node() |
| { |
| if (payload_ == PayloadTraits::empty_payload && children_.size() == 0) { |
| if (parent_ == 0) |
| return; |
| |
| trie* parent = parent_; |
| parent->children_ |
| .erase_and_dispose(*this, |
| trie_delete_disposer()); // delete this; basically, committing a suicide |
| } |
| } |
| |
| // inline std::tuple<const iterator, bool, const iterator> |
| // find (const FullKey &key) const |
| // { |
| // return const_cast<trie*> (this)->find (key); |
| // } |
| |
| /** |
| * @brief Perform the longest prefix match |
| * @param key the key for which to perform the longest prefix match |
| * |
| * @return ->second is true if prefix in ->first is longer than key |
| */ |
| inline std::tuple<iterator, bool, iterator> |
| find(const FullKey& key) |
| { |
| trie* trieNode = this; |
| iterator foundNode = (payload_ != PayloadTraits::empty_payload) ? this : 0; |
| bool reachLast = true; |
| |
| BOOST_FOREACH (const Key& subkey, key) { |
| typename unordered_set::iterator item = trieNode->children_.find(subkey); |
| if (item == trieNode->children_.end()) { |
| reachLast = false; |
| break; |
| } |
| else { |
| trieNode = &(*item); |
| |
| if (trieNode->payload_ != PayloadTraits::empty_payload) |
| foundNode = trieNode; |
| } |
| } |
| |
| return std::make_tuple(foundNode, reachLast, trieNode); |
| } |
| |
| /** |
| * @brief Perform the longest prefix match satisfying preficate |
| * @param key the key for which to perform the longest prefix match |
| * |
| * @return ->second is true if prefix in ->first is longer than key |
| */ |
| template<class Predicate> |
| inline std::tuple<iterator, bool, iterator> |
| find_if(const FullKey& key, Predicate pred) |
| { |
| trie* trieNode = this; |
| iterator foundNode = (payload_ != PayloadTraits::empty_payload) ? this : 0; |
| bool reachLast = true; |
| |
| BOOST_FOREACH (const Key& subkey, key) { |
| typename unordered_set::iterator item = trieNode->children_.find(subkey); |
| if (item == trieNode->children_.end()) { |
| reachLast = false; |
| break; |
| } |
| else { |
| trieNode = &(*item); |
| |
| if (trieNode->payload_ != PayloadTraits::empty_payload && pred(trieNode->payload_)) { |
| foundNode = trieNode; |
| } |
| } |
| } |
| |
| return std::make_tuple(foundNode, reachLast, trieNode); |
| } |
| |
| /** |
| * @brief Find next payload of the sub-trie |
| * @returns end() or a valid iterator pointing to the trie leaf (order is not defined, enumeration |
| * ) |
| */ |
| inline iterator |
| find() |
| { |
| if (payload_ != PayloadTraits::empty_payload) |
| return this; |
| |
| typedef trie<FullKey, PayloadTraits, PolicyHook> trie; |
| for (typename trie::unordered_set::iterator subnode = children_.begin(); |
| subnode != children_.end(); subnode++) |
| // BOOST_FOREACH (trie &subnode, children_) |
| { |
| iterator value = subnode->find(); |
| if (value != 0) |
| return value; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Find next payload of the sub-trie satisfying the predicate |
| * @param pred predicate |
| * @returns end() or a valid iterator pointing to the trie leaf (order is not defined, enumeration |
| * ) |
| */ |
| template<class Predicate> |
| inline const iterator |
| find_if(Predicate pred) |
| { |
| if (payload_ != PayloadTraits::empty_payload && pred(payload_)) |
| return this; |
| |
| typedef trie<FullKey, PayloadTraits, PolicyHook> trie; |
| for (typename trie::unordered_set::iterator subnode = children_.begin(); |
| subnode != children_.end(); subnode++) |
| // BOOST_FOREACH (const trie &subnode, children_) |
| { |
| iterator value = subnode->find_if(pred); |
| if (value != 0) |
| return value; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Find next payload of the sub-trie satisfying the predicate |
| * @param pred predicate |
| * |
| * This version check predicate only for the next level children |
| * |
| * @returns end() or a valid iterator pointing to the trie leaf (order is not defined, enumeration |
| *) |
| */ |
| template<class Predicate> |
| inline const iterator |
| find_if_next_level(Predicate pred) |
| { |
| typedef trie<FullKey, PayloadTraits, PolicyHook> trie; |
| for (typename trie::unordered_set::iterator subnode = children_.begin(); |
| subnode != children_.end(); subnode++) { |
| if (pred(subnode->key())) { |
| return subnode->find(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| iterator |
| end() |
| { |
| return 0; |
| } |
| |
| const_iterator |
| end() const |
| { |
| return 0; |
| } |
| |
| typename PayloadTraits::const_return_type |
| payload() const |
| { |
| return payload_; |
| } |
| |
| typename PayloadTraits::return_type |
| payload() |
| { |
| return payload_; |
| } |
| |
| void |
| set_payload(typename PayloadTraits::insert_type payload) |
| { |
| payload_ = payload; |
| } |
| |
| Key |
| key() const |
| { |
| return key_; |
| } |
| |
| inline void |
| PrintStat(std::ostream& os) const; |
| |
| private: |
| // The disposer object function |
| struct trie_delete_disposer { |
| void |
| operator()(trie* delete_this) |
| { |
| delete delete_this; |
| } |
| }; |
| |
| template<class D> |
| struct array_disposer { |
| void |
| operator()(D* array) |
| { |
| delete[] array; |
| } |
| }; |
| |
| friend std::ostream& operator<<<>(std::ostream& os, const trie& trie_node); |
| |
| public: |
| PolicyHook policy_hook_; |
| |
| private: |
| boost::intrusive::unordered_set_member_hook<> unordered_set_member_hook_; |
| |
| // necessary typedefs |
| typedef trie self_type; |
| typedef boost::intrusive::member_hook<trie, boost::intrusive::unordered_set_member_hook<>, |
| &trie::unordered_set_member_hook_> member_hook; |
| |
| typedef boost::intrusive::unordered_set<trie, member_hook> unordered_set; |
| typedef typename unordered_set::bucket_type bucket_type; |
| typedef typename unordered_set::bucket_traits bucket_traits; |
| |
| template<class T, class NonConstT> |
| friend class trie_iterator; |
| |
| template<class T> |
| friend class trie_point_iterator; |
| |
| //////////////////////////////////////////////// |
| // Actual data |
| //////////////////////////////////////////////// |
| |
| Key key_; ///< name component |
| |
| size_t initialBucketSize_; |
| size_t bucketIncrement_; |
| |
| size_t bucketSize_; |
| typedef boost::interprocess::unique_ptr<bucket_type, array_disposer<bucket_type>> buckets_array; |
| buckets_array buckets_; |
| unordered_set children_; |
| |
| typename PayloadTraits::storage_type payload_; |
| trie* parent_; // to make cleaning effective |
| }; |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| inline std::ostream& |
| operator<<(std::ostream& os, const trie<FullKey, PayloadTraits, PolicyHook>& trie_node) |
| { |
| os << "# " << trie_node.key_ << ((trie_node.payload_ != PayloadTraits::empty_payload) ? "*" : "") |
| << std::endl; |
| typedef trie<FullKey, PayloadTraits, PolicyHook> trie; |
| |
| for (typename trie::unordered_set::const_iterator subnode = trie_node.children_.begin(); |
| subnode != trie_node.children_.end(); subnode++) |
| // BOOST_FOREACH (const trie &subnode, trie_node.children_) |
| { |
| os << "\"" << &trie_node << "\"" |
| << " [label=\"" << trie_node.key_ |
| << ((trie_node.payload_ != PayloadTraits::empty_payload) ? "*" : "") << "\"]\n"; |
| os << "\"" << &(*subnode) << "\"" |
| << " [label=\"" << subnode->key_ |
| << ((subnode->payload_ != PayloadTraits::empty_payload) ? "*" : "") << "\"]" |
| "\n"; |
| |
| os << "\"" << &trie_node << "\"" |
| << " -> " |
| << "\"" << &(*subnode) << "\"" |
| << "\n"; |
| os << *subnode; |
| } |
| |
| return os; |
| } |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| inline void |
| trie<FullKey, PayloadTraits, PolicyHook>::PrintStat(std::ostream& os) const |
| { |
| os << "# " << key_ << ((payload_ != PayloadTraits::empty_payload) ? "*" : "") << ": " |
| << children_.size() << " children" << std::endl; |
| for (size_t bucket = 0, maxbucket = children_.bucket_count(); bucket < maxbucket; bucket++) { |
| os << " " << children_.bucket_size(bucket); |
| } |
| os << "\n"; |
| |
| typedef trie<FullKey, PayloadTraits, PolicyHook> trie; |
| for (typename trie::unordered_set::const_iterator subnode = children_.begin(); |
| subnode != children_.end(); subnode++) |
| // BOOST_FOREACH (const trie &subnode, children_) |
| { |
| subnode->PrintStat(os); |
| } |
| } |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| inline bool |
| operator==(const trie<FullKey, PayloadTraits, PolicyHook>& a, |
| const trie<FullKey, PayloadTraits, PolicyHook>& b) |
| { |
| return a.key_ == b.key_; |
| } |
| |
| template<typename FullKey, typename PayloadTraits, typename PolicyHook> |
| inline std::size_t |
| hash_value(const trie<FullKey, PayloadTraits, PolicyHook>& trie_node) |
| { |
| return boost::hash_value(trie_node.key_); |
| } |
| |
| template<class Trie, class NonConstTrie> // hack for boost < 1.47 |
| class trie_iterator { |
| public: |
| trie_iterator() |
| : trie_(0) |
| { |
| } |
| trie_iterator(typename Trie::iterator item) |
| : trie_(item) |
| { |
| } |
| trie_iterator(Trie& item) |
| : trie_(&item) |
| { |
| } |
| |
| Trie& operator*() |
| { |
| return *trie_; |
| } |
| const Trie& operator*() const |
| { |
| return *trie_; |
| } |
| Trie* operator->() |
| { |
| return trie_; |
| } |
| const Trie* operator->() const |
| { |
| return trie_; |
| } |
| bool |
| operator==(trie_iterator<const Trie, NonConstTrie>& other) const |
| { |
| return (trie_ == other.trie_); |
| } |
| bool |
| operator==(trie_iterator<Trie, NonConstTrie>& other) |
| { |
| return (trie_ == other.trie_); |
| } |
| bool |
| operator!=(trie_iterator<const Trie, NonConstTrie>& other) const |
| { |
| return !(*this == other); |
| } |
| bool |
| operator!=(trie_iterator<Trie, NonConstTrie>& other) |
| { |
| return !(*this == other); |
| } |
| |
| trie_iterator<Trie, NonConstTrie>& |
| operator++(int) |
| { |
| if (trie_->children_.size() > 0) |
| trie_ = &(*trie_->children_.begin()); |
| else |
| trie_ = goUp(); |
| return *this; |
| } |
| |
| trie_iterator<Trie, NonConstTrie>& |
| operator++() |
| { |
| (*this)++; |
| return *this; |
| } |
| |
| private: |
| typedef typename boost::mpl::if_<boost::is_same<Trie, NonConstTrie>, |
| typename Trie::unordered_set::iterator, |
| typename Trie::unordered_set::const_iterator>::type set_iterator; |
| |
| Trie* |
| goUp() |
| { |
| if (trie_->parent_ != 0) { |
| // typename Trie::unordered_set::iterator item = |
| set_iterator item = const_cast<NonConstTrie*>(trie_) |
| ->parent_->children_.iterator_to(const_cast<NonConstTrie&>(*trie_)); |
| item++; |
| if (item != trie_->parent_->children_.end()) { |
| return &(*item); |
| } |
| else { |
| trie_ = trie_->parent_; |
| return goUp(); |
| } |
| } |
| else |
| return 0; |
| } |
| |
| private: |
| Trie* trie_; |
| }; |
| |
| template<class Trie> |
| class trie_point_iterator { |
| private: |
| typedef typename boost::mpl::if_<boost::is_same<Trie, const Trie>, |
| typename Trie::unordered_set::const_iterator, |
| typename Trie::unordered_set::iterator>::type set_iterator; |
| |
| public: |
| trie_point_iterator() |
| : trie_(0) |
| { |
| } |
| trie_point_iterator(typename Trie::iterator item) |
| : trie_(item) |
| { |
| } |
| trie_point_iterator(Trie& item) |
| { |
| if (item.children_.size() != 0) |
| trie_ = &*item.children_.begin(); |
| else |
| trie_ = 0; |
| } |
| |
| Trie& operator*() |
| { |
| return *trie_; |
| } |
| const Trie& operator*() const |
| { |
| return *trie_; |
| } |
| Trie* operator->() |
| { |
| return trie_; |
| } |
| const Trie* operator->() const |
| { |
| return trie_; |
| } |
| bool |
| operator==(trie_point_iterator<const Trie>& other) const |
| { |
| return (trie_ == other.trie_); |
| } |
| bool |
| operator==(trie_point_iterator<Trie>& other) |
| { |
| return (trie_ == other.trie_); |
| } |
| bool |
| operator!=(trie_point_iterator<const Trie>& other) const |
| { |
| return !(*this == other); |
| } |
| bool |
| operator!=(trie_point_iterator<Trie>& other) |
| { |
| return !(*this == other); |
| } |
| |
| trie_point_iterator<Trie>& |
| operator++(int) |
| { |
| if (trie_->parent_ != 0) { |
| set_iterator item = trie_->parent_->children_.iterator_to(*trie_); |
| item++; |
| if (item == trie_->parent_->children_.end()) |
| trie_ = 0; |
| else |
| trie_ = &*item; |
| } |
| else { |
| trie_ = 0; |
| } |
| return *this; |
| } |
| |
| trie_point_iterator<Trie>& |
| operator++() |
| { |
| (*this)++; |
| return *this; |
| } |
| |
| private: |
| Trie* trie_; |
| }; |
| |
| } // ndnSIM |
| } // ndn |
| } // ns3 |
| |
| /// @endcond |
| |
| #endif // TRIE_H_ |