--- /dev/null
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef _DOMAINTREE_H
+#define _DOMAINTREE_H 1
+
+//! \file datasrc/memory/domaintree.h
+///
+/// \note The purpose of the DomainTree is to provide a generic map with
+/// domain names as the key that can be used by various BIND 10 modules or
+/// even by other applications. However, because of some unresolved design
+/// issue, the design and interface are not fixed, and DomainTree isn't ready
+/// to be used as a base data structure by other modules.
+
+#include <exceptions/exceptions.h>
+#include <util/memory_segment.h>
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/static_assert.hpp>
+
+#include <ostream>
+#include <algorithm>
+#include <cassert>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// Forward declare DomainTree class here is convinent for following friend
+/// class declare inside DomainTreeNode and DomainTreeNodeChain
+template <typename T>
+class DomainTree;
+
+/// \brief \c DomainTreeNode is used by DomainTree to store any data related to one domain
+/// name.
+///
+/// This is meant to be used only from DomainTree. It is meaningless to inherit it
+/// or create instances of it from elsewhere. For that reason, the constructor
+/// (and the allocator, see below) is private.
+///
+/// It serves three roles. One is to keep structure of the \c DomainTree as a
+/// red-black tree. For that purpose, it has left, right and parent pointers
+/// and color member. These are private and accessed only from within the tree.
+///
+/// The second one is to store data for one domain name. The data related
+/// functions can be used to access and set the data.
+///
+/// The third role is to keep the hierarchy of domains. The down pointer
+/// points to a subtree of subdomains. The parent pointer of a subtree's
+/// root node points to the parent leaf of the upper tree.
+///
+/// One special kind of node is non-terminal node. It has subdomains with
+/// RRsets, but doesn't have any RRsets itself.
+///
+/// In order to keep memory footprint as small as possible, the node data
+/// are heavily packed. Specifically, some internal node properties (such as
+/// the node color) are encoded as part of "flags", some of the flag bits
+/// can also be set by the user application. Each node is associated with
+/// a sequence of domain name labels, which is essentially the search/insert
+/// key for the node (see also the description of DomainTree). This is encoded
+/// as opaque binary immediately following the main node object. The size
+/// of the allocated space for the labels data is encoded by borrowing some
+/// bits of the "flags" field.
+template <typename T>
+class DomainTreeNode : public boost::noncopyable {
+private:
+ /// The DomainTreeNode is meant for use from within DomainTree, so it has access to
+ /// it.
+ friend class DomainTree<T>;
+
+ /// \brief Just a type alias
+ ///
+ /// We are going to use a lot of these offset pointers here and they
+ /// have a long name.
+ typedef boost::interprocess::offset_ptr<DomainTreeNode<T> > DomainTreeNodePtr;
+
+ /// \name Constructors
+ ///
+ /// \note The existence of a DomainTreeNode without a DomainTree is meaningless.
+ /// Therefore the constructors are private.
+ //@{
+
+ /// \brief Constructor from normal nodes.
+ DomainTreeNode(size_t labels_capacity);
+
+ /// \brief Destructor
+ ~DomainTreeNode();
+
+ //@}
+
+ /// \brief Accessor to the memory region for node labels.
+ ///
+ /// The only valid usage of the returned pointer is to pass it to the
+ /// corresponding constructor of \c dns::LabelSequence.
+ const void* getLabelsData() const { return (this + 1); }
+
+ /// \brief Accessor to the memory region for node labels, mutable version.
+ ///
+ /// The only valid usage of the returned pointer is to pass it to
+ /// \c LabelSequence::serialize() with the node's labels_capacity_ member
+ /// (which should be sufficiently large for the \c LabelSequence in that
+ /// context).
+ void* getLabelsData() { return (this + 1); }
+
+ /// \brief Allocate and construct \c DomainTreeNode
+ ///
+ /// This static method allocates memory for a new \c DomainTreeNode object
+ /// from the given memory segment, constructs the object, and returns
+ /// a pointer to it.
+ ///
+ /// \throw std::bad_alloc Memory allocation fails.
+ ///
+ /// \param mem_sgmt A \c MemorySegment from which memory for the new
+ /// \c DomainTreeNode is allocated.
+ static DomainTreeNode<T>* create(util::MemorySegment& mem_sgmt,
+ const dns::LabelSequence& labels)
+ {
+ const size_t labels_len = labels.getSerializedLength();
+ void* p = mem_sgmt.allocate(sizeof(DomainTreeNode<T>) + labels_len);
+ DomainTreeNode<T>* node = new(p) DomainTreeNode<T>(labels_len);
+ labels.serialize(node->getLabelsData(), labels_len);
+ return (node);
+ }
+
+ /// \brief Destruct and deallocate \c DomainTreeNode
+ ///
+ /// \throw none
+ ///
+ /// \param mem_sgmt The \c MemorySegment that allocated memory for
+ /// \c rbnode.
+ /// \param rbnode A non NULL pointer to a valid \c DomainTreeNode object
+ /// that was originally created by the \c create() method (the behavior
+ /// is undefined if this condition isn't met).
+ static void destroy(util::MemorySegment& mem_sgmt, DomainTreeNode<T>* rbnode) {
+ const size_t labels_capacity = rbnode->labels_capacity_;
+ rbnode->~DomainTreeNode<T>();
+ mem_sgmt.deallocate(rbnode, sizeof(DomainTreeNode<T>) + labels_capacity);
+ }
+
+ /// \brief Reset node's label sequence to a new one.
+ ///
+ /// The new labels must be a sub sequence of the current label sequence;
+ /// otherwise the serialize() method will throw an exception.
+ void resetLabels(const dns::LabelSequence& labels) {
+ labels.serialize(getLabelsData(), labels_capacity_);
+ }
+
+public:
+ /// \brief Alias for shared pointer to the data.
+ typedef boost::shared_ptr<T> NodeDataPtr;
+
+ /// Node flags.
+ ///
+ /// Each flag value defines a non default property for a specific node.
+ /// These are defined as bitmask type values for the convenience of
+ /// internal implementation, but applications are expected to use
+ /// each flag separately via the enum definitions.
+ ///
+ /// All (settable) flags are off by default; they must be explicitly
+ /// set to on by the \c setFlag() method.
+ enum Flags {
+ FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
+ FLAG_RED = 2, ///< Node color; 1 if node is red, 0 if node is black.
+ FLAG_SUBTREE_ROOT = 4, ///< Set if the node is the root of a subtree
+ FLAG_USER1 = 0x400000U, ///< Application specific flag
+ FLAG_USER2 = 0x200000U, ///< Application specific flag
+ FLAG_USER3 = 0x100000U, ///< Application specific flag
+ FLAG_MAX = 0x400000U // for integrity check
+ };
+private:
+ // Some flag values are expected to be used for internal purposes
+ // (e.g., representing the node color) in future versions, so we
+ // limit the settable flags via the \c setFlag() method to those
+ // explicitly defined in \c Flags. This constant represents all
+ // such flags.
+ static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1 |
+ FLAG_USER2 | FLAG_USER3);
+
+public:
+
+ /// \name Getter functions.
+ //@{
+ /// \brief Return the name of current node.
+ ///
+ /// It's relative to its containing node.
+ ///
+ /// To get the absolute name of one node, the node path from the top node
+ /// to current node has to be recorded.
+ ///
+ /// \note We should eventually deprecate this method and revise all its
+ /// usage with \c getLabels(). At this point the only user of this method
+ /// is getAbsoluteName()::getAbsoluteName(), which would have to be revised
+ /// using \c LabelSequence. Until then we keep this interface as a
+ /// simplest form of wrapper; it's not efficient, but should be replaced
+ /// before we need to worry about that.
+ const isc::dns::Name getName() const {
+ return (dns::Name(dns::LabelSequence(getLabelsData()).toText()));
+ }
+
+ /// \brief Return the label sequence of the node.
+ ///
+ /// This method returns the label sequence corresponding to this node
+ /// in the form of \c dns::LabelSequence object. Any modification to
+ /// the tree can invalidate the returned \c LabelSequence object or copy
+ /// of it; in general, it's expected to be used in a very limited scope.
+ dns::LabelSequence getLabels() const {
+ return (dns::LabelSequence(getLabelsData()));
+ }
+
+ /// \brief Return the data stored in this node.
+ ///
+ /// You should not delete the data, it is handled by shared pointers.
+ NodeDataPtr& getData() { return (data_); }
+ /// \brief Return the data stored in this node.
+ const NodeDataPtr& getData() const { return (data_); }
+
+ /// \brief return whether the node has related data.
+ ///
+ /// There can be empty nodes inside the DomainTree. They are usually the
+ /// non-terminal domains, but it is possible (yet probably meaningless)
+ /// empty nodes anywhere.
+ bool isEmpty() const { return (data_.get() == NULL); }
+ //@}
+
+ /// \name Setter functions.
+ //@{
+ /// \brief Set the data stored in the node.
+ void setData(const NodeDataPtr& data) { data_ = data; }
+ //@}
+
+ /// \name Node flag manipulation methods
+ //@{
+ /// Get the status of a node flag.
+ ///
+ /// This method returns whether the given node flag is set (enabled)
+ /// on the node. The \c flag parameter is expected to be one of the
+ /// defined \c Flags constants. For simplicity, the method interface
+ /// does not prohibit passing an undefined flag or combined flags, but
+ /// the return value in such a case will be meaningless for the caller
+ /// (an application would have to use an ugly cast for such an unintended
+ /// form of call, which will hopefully avoid accidental misuse).
+ ///
+ /// \exception None
+ /// \param flag The flag to be tested.
+ /// \return \c true if the \c flag is set; \c false otherwise.
+ bool getFlag(Flags flag) const {
+ return ((flags_ & flag) != 0);
+ }
+
+ /// Set or clear a node flag.
+ ///
+ /// This method changes the status of the specified node flag to either
+ /// "on" (enabled) or "off" (disabled). The new status is specified by
+ /// the \c on parameter.
+ /// Like the \c getFlag() method, \c flag is expected to be one of the
+ /// defined \c Flags constants. If an undefined or unsettable flag is
+ /// specified, \c isc::InvalidParameter exception will be thrown.
+ ///
+ /// \exception isc::InvalidParameter Unsettable flag is specified
+ /// \exception None otherwise
+ /// \param flag The node flag to be changed.
+ /// \param on If \c true, set the flag to on; otherwise set it to off.
+ void setFlag(Flags flag, bool on = true) {
+ if ((flag & ~SETTABLE_FLAGS) != 0) {
+ isc_throw(isc::InvalidParameter,
+ "Unsettable DomainTree flag is being set");
+ }
+ if (on) {
+ flags_ |= flag;
+ } else {
+ flags_ &= ~flag;
+ }
+ }
+ //@}
+
+private:
+ /// \name Callback related methods
+ ///
+ /// See the description of \c DomainTree<T>::find() at \ref callback
+ /// about callbacks.
+ ///
+ /// These methods never throw an exception.
+ //@{
+ /// Return if callback is enabled at the node.
+ //@}
+
+
+ /// \brief Define rbnode color
+ enum DomainTreeNodeColor {BLACK, RED};
+
+ /// \brief Returns the color of this node
+ DomainTreeNodeColor getColor() const {
+ if ((flags_ & FLAG_RED) != 0) {
+ return (RED);
+ } else {
+ return (BLACK);
+ }
+ }
+
+ /// \brief Sets the color of this node
+ void setColor(const DomainTreeNodeColor color) {
+ if (color == RED) {
+ flags_ |= FLAG_RED;
+ } else {
+ flags_ &= ~FLAG_RED;
+ }
+ }
+
+ void setSubTreeRoot(bool root) {
+ if (root) {
+ flags_ |= FLAG_SUBTREE_ROOT;
+ } else {
+ flags_ &= ~FLAG_SUBTREE_ROOT;
+ }
+ }
+
+ bool isSubTreeRoot() const {
+ return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
+ }
+
+public:
+ /// \brief returns the parent of the root of its subtree
+ ///
+ /// This method takes a node and returns the parent of the root of
+ /// its subtree (i.e, it returns the node's immediate ancestor in
+ /// the tree-of-tree hierarchy). If the node is at the top level
+ /// (which should be absolute), it will return \c NULL.
+ ///
+ /// This method never throws an exception.
+ const DomainTreeNode<T>* getUpperNode() const;
+
+private:
+ /// \brief return the next node which is bigger than current node
+ /// in the same subtree
+ ///
+ /// The next successor for this node is the next bigger node in terms of
+ /// the DNSSEC order relation within the same single subtree.
+ /// Note that it may NOT be the next bigger node in the entire DomainTree;
+ /// DomainTree is a tree in tree, and the real next node may reside in
+ /// an upper or lower subtree of the subtree where this node belongs.
+ /// For example, if this node has a sub domain, the real next node is
+ /// the smallest node in the sub domain tree.
+ ///
+ /// If this node is the biggest node within the subtree, this method
+ /// returns \c NULL.
+ ///
+ /// This method never throws an exception.
+ const DomainTreeNode<T>* successor() const;
+
+ /// \brief return the next node which is smaller than current node
+ /// in the same subtree
+ ///
+ /// The predecessor for this node is the next smaller node in terms of
+ /// the DNSSEC order relation within the same single subtree.
+ /// Note that it may NOT be the next smaller node in the entire DomainTree;
+ /// DomainTree is a tree in tree, and the real next node may reside in
+ /// an upper or lower subtree of the subtree where this node belongs.
+ /// For example, if the predecessor node has a sub domain, the real next
+ /// node is the largest node in the sub domain tree.
+ ///
+ /// If this node is the smallest node within the subtree, this method
+ /// returns \c NULL.
+ ///
+ /// This method never throws an exception.
+ const DomainTreeNode<T>* predecessor() const;
+
+ /// \brief private shared implementation of successor and predecessor
+ ///
+ /// As the two mentioned functions are merely mirror images of each other,
+ /// it makes little sense to keep both versions. So this is the body of the
+ /// functions and we call it with the correct pointers.
+ ///
+ /// Not to be called directly, not even by friends.
+ ///
+ /// The overhead of the member pointers should be optimised out, as this
+ /// will probably get completely inlined into predecessor and successor
+ /// methods.
+ const DomainTreeNode<T>*
+ abstractSuccessor(typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,
+ typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*right)
+ const;
+
+ /// \name Data to maintain the rbtree structure.
+ ///
+ /// We keep them as offset pointers. This is part of a future plan, when we
+ /// want to share the image of the tree between multiple processes.
+ /// However, whenever we have a chance, we switch to bare pointers during
+ /// the processing. The pointers on stack are never shared and the offset
+ /// pointers have non-trivial performance impact.
+ //@{
+ DomainTreeNodePtr parent_;
+ /// \brief Access the parent_ as bare pointer.
+ DomainTreeNode<T>* getParent() {
+ return (parent_.get());
+ }
+ /// \brief Access the parent_ as bare pointer, const.
+ const DomainTreeNode<T>* getParent() const {
+ return (parent_.get());
+ }
+ DomainTreeNodePtr left_;
+ /// \brief Access the left_ as bare pointer.
+ DomainTreeNode<T>* getLeft() {
+ return (left_.get());
+ }
+ /// \brief Access the left_ as bare pointer, const.
+ const DomainTreeNode<T>* getLeft() const {
+ return (left_.get());
+ }
+ DomainTreeNodePtr right_;
+ /// \brief Access the right_ as bare pointer.
+ DomainTreeNode<T>* getRight() {
+ return (right_.get());
+ }
+ /// \brief Access the right_ as bare pointer, const.
+ const DomainTreeNode<T>* getRight() const {
+ return (right_.get());
+ }
+ //@}
+
+ /// \brief Data stored here.
+ NodeDataPtr data_;
+
+ /// \brief The subdomain tree.
+ ///
+ /// This points to the root node of trees of subdomains of this domain.
+ ///
+ /// \par Adding down pointer to \c DomainTreeNode has two purposes:
+ /// \li Accelerate the search process, with sub domain tree, it splits the
+ /// big flat tree into several hierarchy trees.
+ /// \li It saves memory usage as it allows storing only relative names,
+ /// avoiding storage of the same domain labels multiple times.
+ DomainTreeNodePtr down_;
+ /// \brief Access the down_ as bare pointer.
+ DomainTreeNode<T>* getDown() {
+ return (down_.get());
+ }
+ /// \brief Access the down_ as bare pointer, const.
+ const DomainTreeNode<T>* getDown() const {
+ return (down_.get());
+ }
+
+ /// \brief Internal or user-configurable flags of node's properties.
+ ///
+ /// See the \c Flags enum for available flags.
+ ///
+ /// For memory efficiency reasons, we only use a subset of the 32-bit
+ /// space, and use the rest to store the allocated size for the node's
+ /// label sequence data.
+ uint32_t flags_ : 23; // largest flag being 0x400000
+ BOOST_STATIC_ASSERT((1 << 23) > FLAG_MAX); // assumption check
+
+ const uint32_t labels_capacity_ : 9; // size for labelseq; range is 0..511
+ // Make sure the reserved space for labels_capacity_ is sufficiently
+ // large. In effect, we use the knowledge of the implementation of the
+ // serialization, but we still only use its public interface, and the
+ // public interface of this class doesn't rely on this assumption.
+ // So we can change this implementation without affecting its users if
+ // a future change to LabelSequence breaks this assumption.
+ BOOST_STATIC_ASSERT((1 << 9) > dns::LabelSequence::MAX_SERIALIZED_LENGTH);
+};
+
+template <typename T>
+DomainTreeNode<T>::DomainTreeNode(size_t labels_capacity) :
+ parent_(NULL),
+ left_(NULL),
+ right_(NULL),
+ down_(NULL),
+ flags_(FLAG_RED | FLAG_SUBTREE_ROOT),
+ labels_capacity_(labels_capacity)
+{
+}
+
+template <typename T>
+DomainTreeNode<T>::~DomainTreeNode() {
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() const {
+ const DomainTreeNode<T>* current = this;
+
+ // current would never be equal to NULL here (in a correct tree
+ // implementation)
+ while (!current->isSubTreeRoot()) {
+ current = current->getParent();
+ }
+
+ return (current->getParent());
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::abstractSuccessor(typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,
+ typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*right)
+ const
+{
+ // This function is written as a successor. It becomes predecessor if
+ // the left and right pointers are swapped. So in case of predecessor,
+ // the left pointer points to right and vice versa. Don't get confused
+ // by the idea, just imagine the pointers look into a mirror.
+
+ const DomainTreeNode<T>* current = this;
+ // If it has right node, the successor is the left-most node of the right
+ // subtree.
+ if ((current->*right).get() != NULL) {
+ current = (current->*right).get();
+ const DomainTreeNode<T>* left_n;
+ while ((left_n = (current->*left).get()) != NULL) {
+ current = left_n;
+ }
+ return (current);
+ }
+
+ // Otherwise go up until we find the first left branch on our path to
+ // root. If found, the parent of the branch is the successor.
+ // Otherwise, we return the null node
+ const DomainTreeNode<T>* parent = current->getParent();
+ while ((!current->isSubTreeRoot()) &&
+ (current == (parent->*right).get())) {
+ current = parent;
+ parent = parent->getParent();
+ }
+
+ if (!current->isSubTreeRoot()) {
+ return (parent);
+ } else {
+ return (NULL);
+ }
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::successor() const {
+ return (abstractSuccessor(&DomainTreeNode<T>::left_, &DomainTreeNode<T>::right_));
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::predecessor() const {
+ // Swap the left and right pointers for the abstractSuccessor
+ return (abstractSuccessor(&DomainTreeNode<T>::right_, &DomainTreeNode<T>::left_));
+}
+
+/// \brief DomainTreeNodeChain stores detailed information of \c DomainTree::find()
+/// result.
+///
+/// - The \c DomainTreeNode that was last compared with the search name, and
+/// the comparison result at that point in the form of
+/// \c isc::dns::NameComparisonResult.
+/// - A sequence of nodes that forms a path to the found node.
+///
+/// The comparison result can be used to handle some rare cases such as
+/// empty node processing.
+/// The node sequence keeps track of the nodes to reach any given node from
+/// the root of DomainTree.
+///
+/// Currently, DomainTreeNode does not have "up" pointers in them (i.e., back pointers
+/// from the root of one level of tree of trees to the node in the parent
+/// tree whose down pointer points to that root node) for memory usage
+/// reasons, so there is no other way to find the path back to the root from
+/// any given DomainTreeNode.
+///
+/// \note This design may change in future versions. In particular, it's
+/// quite likely we want to have that pointer if we want to optimize name
+/// compression by exploiting the structure of the zone. If and when that
+/// happens we should also revisit the need for the chaining.
+/// Also, the class name may not be appropriate now that it contains other
+/// information than a node "chain", and the chain itself may even be
+/// deprecated. Something like "DomainTreeFindContext" may be a better name.
+/// This point should be revisited later.
+///
+/// DomainTreeNodeChain is constructed and manipulated only inside the \c DomainTree
+/// class.
+/// \c DomainTree uses it as an inner data structure to iterate over the whole
+/// DomainTree.
+/// This is the reason why manipulation methods such as \c push() and \c pop()
+/// are private (and not shown in the doxygen document).
+template <typename T>
+class DomainTreeNodeChain {
+ /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has
+ /// knowledge to manipulate it.
+ friend class DomainTree<T>;
+public:
+ /// \name Constructors and Assignment Operator.
+ ///
+ /// \note The copy constructor and the assignment operator are
+ /// intentionally defined as private, making this class non copyable.
+ /// This may have to be changed in a future version with newer need.
+ /// For now we explicitly disable copy to avoid accidental copy happens
+ /// unintentionally.
+ //{@
+ /// The default constructor.
+ ///
+ /// \exception None
+ DomainTreeNodeChain() : node_count_(0), last_compared_(NULL),
+ // XXX: meaningless initial values:
+ last_comparison_(0, 0,
+ isc::dns::NameComparisonResult::EQUAL)
+ {}
+
+private:
+ DomainTreeNodeChain(const DomainTreeNodeChain<T>&);
+ DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
+ //@}
+
+public:
+ /// Clear the state of the chain.
+ ///
+ /// This method re-initializes the internal state of the chain so that
+ /// it can be reused for subsequent operations.
+ ///
+ /// \exception None
+ void clear() {
+ node_count_ = 0;
+ last_compared_ = NULL;
+ }
+
+ /// Return the \c DomainTreeNode that was last compared in \c DomainTree::find().
+ ///
+ /// If this chain has been passed to \c DomainTree::find() and there has
+ /// been name comparison against the search name, the last compared
+ /// \c DomainTreeNode is recorded within the chain. This method returns that
+ /// node.
+ /// If \c DomainTree::find() hasn't been called with this chain or name
+ /// comparison hasn't taken place (which is possible if the tree is empty),
+ /// this method returns \c NULL.
+ ///
+ /// \exception None
+ const DomainTreeNode<T>* getLastComparedNode() const {
+ return (last_compared_);
+ }
+
+ /// Return the result of last name comparison in \c DomainTree::find().
+ ///
+ /// Like \c getLastComparedNode(), \c DomainTree::find() records the result
+ /// of the last name comparison in the chain. This method returns the
+ /// result.
+ /// The return value of this method is only meaningful when comparison
+ /// has taken place, i.e, when \c getLastComparedNode() would return a
+ /// non \c NULL value.
+ ///
+ /// \exception None
+ const isc::dns::NameComparisonResult& getLastComparisonResult() const {
+ return (last_comparison_);
+ }
+
+ /// \brief Return the number of levels stored in the chain.
+ ///
+ /// It's equal to the number of nodes in the chain; for an empty
+ /// chain, 0 will be returned.
+ ///
+ /// \exception None
+ unsigned int getLevelCount() const { return (node_count_); }
+
+ /// \brief return the absolute name for the node which this
+ /// \c DomainTreeNodeChain currently refers to.
+ ///
+ /// The chain must not be empty.
+ ///
+ /// \exception isc::BadValue the chain is empty.
+ /// \exception std::bad_alloc memory allocation for the new name fails.
+ isc::dns::Name getAbsoluteName() const {
+ if (isEmpty()) {
+ isc_throw(isc::BadValue,
+ "DomainTreeNodeChain::getAbsoluteName is called on an empty "
+ "chain");
+ }
+
+ const DomainTreeNode<T>* top_node = top();
+ isc::dns::Name absolute_name = top_node->getName();
+ int node_count = node_count_ - 1;
+ while (node_count > 0) {
+ top_node = nodes_[node_count - 1];
+ absolute_name = absolute_name.concatenate(top_node->getName());
+ --node_count;
+ }
+ return (absolute_name);
+ }
+
+private:
+ // the following private functions check invariants about the internal
+ // state using assert() instead of exception. The state of a chain
+ // can only be modified by operations within this file, so if any of the
+ // assumptions fails it means an internal bug.
+
+ /// \brief return whether node chain has node in it.
+ ///
+ /// \exception None
+ bool isEmpty() const { return (node_count_ == 0); }
+
+ /// \brief return the top node for the node chain
+ ///
+ /// DomainTreeNodeChain store all the nodes along top node to
+ /// root node of DomainTree
+ ///
+ /// \exception None
+ const DomainTreeNode<T>* top() const {
+ assert(!isEmpty());
+ return (nodes_[node_count_ - 1]);
+ }
+
+ /// \brief pop the top node from the node chain
+ ///
+ /// After pop, up/super node of original top node will be
+ /// the top node
+ ///
+ /// \exception None
+ void pop() {
+ assert(!isEmpty());
+ --node_count_;
+ }
+
+ /// \brief add the node into the node chain
+ ///
+ /// If the node chain isn't empty, the node should be
+ /// the sub domain of the original top node in node chain
+ /// otherwise the node should be the root node of DomainTree.
+ ///
+ /// \exception None
+ void push(const DomainTreeNode<T>* node) {
+ assert(node_count_ < RBT_MAX_LEVEL);
+ nodes_[node_count_++] = node;
+ }
+
+private:
+ // The max label count for one domain name is Name::MAX_LABELS (128).
+ // Since each node in rbtree stores at least one label, it's also equal
+ // to the possible maximum level.
+ const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
+
+ int node_count_;
+ const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
+ const DomainTreeNode<T>* last_compared_;
+ isc::dns::NameComparisonResult last_comparison_;
+};
+
+
+// note: the following class description is documented using multiline comments
+// because the verbatim diagram contain a backslash, which could be interpreted
+// as escape of newline in singleline comment.
+/**
+ * \brief \c DomainTree class represents all the domains with the same suffix.
+ * It can be used to store the domains in one zone, for example.
+ *
+ * DomainTree is a generic map from domain names to any kind of data. Internally,
+ * it uses a red-black tree. However, it isn't one tree containing everything.
+ * Subdomains are trees, so this structure is recursive - trees inside trees.
+ * But, from the interface point of view, it is opaque data structure.
+ *
+ * \c DomainTree splits the domain space into hierarchy red black trees; nodes
+ * in one tree has the same base name. The benefit of this struct is that:
+ * - Enhances the query performace compared with one big flat red black tree.
+ * - Decreases the memory footprint, as it doesn't store the suffix labels
+ * multiple times.
+ *
+ * Depending on different usage, rbtree will support different search policies.
+ * Whether to return an empty node to end user is one policy among them.
+ * The default policy is to NOT return an empty node to end user;
+ * to change the behavior, specify \c true for the constructor parameter
+ * \c returnEmptyNode.
+ * \note The search policy only affects the \c find() behavior of DomainTree.
+ * When inserting one name into DomainTree, if the node with the name already
+ * exists in the DomainTree and it's an empty node which doesn't have any data,
+ * the \c insert() method will still return \c ALREADYEXISTS regardless of
+ * the search policy.
+ *
+ * \anchor diagram
+ *
+ * with the following names:
+ * - a
+ * - b
+ * - c
+ * - x.d.e.f
+ * - z.d.e.f
+ * - g.h
+ * - o.w.y.d.e.f
+ * - p.w.y.d.e.f
+ * - q.w.y.d.e.f
+ *
+ * the tree will look like:
+ * \verbatim
+ .
+ |
+ b
+ / \
+ a d.e.f
+ /|\
+ c | g.h
+ |
+ w.y
+ /|\
+ x | z
+ |
+ p
+ / \
+ o q
+ \endverbatim
+ * \todo
+ * - add remove interface
+ */
+template <typename T>
+class DomainTree : public boost::noncopyable {
+ friend class DomainTreeNode<T>;
+public:
+ /// \brief The return value for the \c find() and insert() methods
+ enum Result {
+ SUCCESS, ///< Insert was successful
+ /// \brief The node returned from find mathes exactly the name given
+ EXACTMATCH,
+ PARTIALMATCH, ///< A superdomain node was found
+ NOTFOUND, ///< Not even any superdomain was found
+ /// \brief Returned by insert() if a node of the name already exists
+ ALREADYEXISTS,
+ };
+
+ /// \brief Allocate and construct \c DomainTree
+ ///
+ /// This static method allocates memory for a new \c DomainTree object
+ /// from the given memory segment, constructs the object, and returns
+ /// a pointer to it.
+ ///
+ /// \throw std::bad_alloc Memory allocation fails.
+ ///
+ /// \param mem_sgmt A \c MemorySegment from which memory for the new
+ /// \c DomainTree is allocated.
+ static DomainTree* create(util::MemorySegment& mem_sgmt,
+ bool return_empty_node = false)
+ {
+ void* p = mem_sgmt.allocate(sizeof(DomainTree<T>));
+ return (new(p) DomainTree<T>(return_empty_node));
+ }
+
+ /// \brief Destruct and deallocate \c DomainTree
+ ///
+ /// This method also destroys and deallocates all nodes inserted to the
+ /// tree.
+ ///
+ /// \note The memory segment (\c mem_sgmt) must be the same one that
+ /// was originally used to allocate memory for the tree (and for all
+ /// nodes inserted to the tree, due to the requirement of \c insert()),
+ /// since the tree itself doesn't maintain a reference to the segment.
+ /// This is not a robust interface, but since we plan to share the tree
+ /// structure by multiple processes via shared memory or possibly allow
+ /// the memory image to be dumped to a file for later reload, there
+ /// doesn't seem to be an easy way to store such reference in the data
+ /// itself. We should probably consider a wrapper interface that
+ /// encapsulates the corresponding segment and always use it for any
+ /// allocation/deallocation of tree related data (the tree itself, their
+ /// nodes, and node data) to keep the usage as safe as possible.
+ ///
+ /// \throw none
+ ///
+ /// \param mem_sgmt The \c MemorySegment that allocated memory for
+ /// \c rbtree and for all nodes inserted to the tree.
+ /// \param rbtree A non NULL pointer to a valid \c DomainTree object
+ /// that was originally created by the \c create() method (the behavior
+ /// is undefined if this condition isn't met).
+ static void destroy(util::MemorySegment& mem_sgmt, DomainTree<T>* rbtree) {
+ rbtree->deleteAllNodes(mem_sgmt);
+ rbtree->~DomainTree<T>();
+ mem_sgmt.deallocate(rbtree, sizeof(DomainTree<T>));
+ }
+
+private:
+ /// \name Constructor and Destructor
+ //@{
+ /// \brief The constructor.
+ ///
+ /// An object of this class is always expected to be created by the
+ /// allocator (\c create()), so the constructor is hidden as private.
+ ///
+ /// It never throws an exception.
+ explicit DomainTree(bool returnEmptyNode = false);
+
+ /// \brief The destructor.
+ ///
+ /// An object of this class is always expected to be destroyed explicitly
+ /// by \c destroy(), so the constructor is hidden as private.
+ ///
+ /// \note DomainTree is not intended to be inherited so the destructor
+ /// is not virtual
+ ~DomainTree();
+ //@}
+
+public:
+
+ /// \name Find methods
+ ///
+ /// \brief Find the node that gives a longest match against the given name.
+ ///
+ /// \anchor find
+ ///
+ /// These methods search the DomainTree for a node whose name is longest
+ /// against name. The found node, if any, is returned via the node pointer.
+ ///
+ /// By default, nodes that don't have data (see DomainTreeNode::isEmpty) are
+ /// ignored and the result can be NOTFOUND even if there's a node whose
+ /// name matches. If the \c DomainTree is constructed with its
+ /// \c returnEmptyNode parameter being \c true, empty nodes will also
+ /// be match candidates.
+ ///
+ /// \note Even when \c returnEmptyNode is \c true, not all empty nodes
+ /// in terms of the DNS protocol may necessarily be found by this method.
+ /// For example, in the \ref diagram shown in the class description,
+ /// the name y.d.e.f is logically contained in the tree as part of the
+ /// node w.y, but the \c find() variants cannot find the former for
+ /// the search key of y.d.e.f, no matter how the \c DomainTree is constructed.
+ /// The caller of this method must use a different way to identify the
+ /// hidden match when necessary.
+ ///
+ /// These methods involve operations on names that can throw an exception.
+ /// If that happens the exception will be propagated to the caller.
+ /// The callback function should generally not throw an exception, but
+ /// if it throws, the exception will be propagated to the caller.
+ ///
+ /// The \c name parameter says what should be found. The node parameter
+ /// is output-only, and in case of EXACTMATCH or PARTIALMATCH, it is set
+ /// to a pointer to the found node.
+ ///
+ /// They return:
+ /// - EXACTMATCH when a node with the same name as requested exists.
+ /// - PARTIALMATCH when a node with the same name does not exist (or is
+ /// empty), but there's a (nonempty) superdomain of the requested one.
+ /// The superdomain with longest name is returned through the node
+ /// parameter. Beware that if you store a zone in the tree, you may get
+ /// PARTIALMATCH with zone apex when the given domain name is not there.
+ /// You should not try to delegate into another zone in that case.
+ /// - NOTFOUND if there's no node with the same name nor any superdomain
+ /// of it. In that case, node parameter is left intact.
+ //@{
+
+ /// \brief Simple find.
+ ///
+ /// Acts as described in the \ref find section.
+ Result find(const isc::dns::Name& name, DomainTreeNode<T>** node) const {
+ DomainTreeNodeChain<T> node_path;
+ const isc::dns::LabelSequence ls(name);
+ return (find<void*>(ls, node, node_path, NULL, NULL));
+ }
+
+ /// \brief Simple find returning immutable node.
+ ///
+ /// Acts as described in the \ref find section, but returns immutable node
+ /// pointer.
+ Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node) const {
+ DomainTreeNodeChain<T> node_path;
+ DomainTreeNode<T> *target_node = NULL;
+ const isc::dns::LabelSequence ls(name);
+ Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
+ if (ret != NOTFOUND) {
+ *node = target_node;
+ }
+ return (ret);
+ }
+
+ /// \brief Simple find, with node_path tracking
+ ///
+ /// Acts as described in the \ref find section.
+ Result find(const isc::dns::Name& name, DomainTreeNode<T>** node,
+ DomainTreeNodeChain<T>& node_path) const
+ {
+ const isc::dns::LabelSequence ls(name);
+ return (find<void*>(ls, node, node_path, NULL, NULL));
+ }
+
+ /// \brief Simple find returning immutable node, with node_path tracking
+ ///
+ /// Acts as described in the \ref find section, but returns immutable node
+ /// pointer.
+ Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
+ DomainTreeNodeChain<T>& node_path) const
+ {
+ DomainTreeNode<T> *target_node = NULL;
+ const isc::dns::LabelSequence ls(name);
+ Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
+ if (ret != NOTFOUND) {
+ *node = target_node;
+ }
+ return (ret);
+ }
+
+ /// \brief Simple find returning immutable node.
+ ///
+ /// Acts as described in the \ref find section, but returns immutable
+ /// node pointer.
+ template <typename CBARG>
+ Result find(const isc::dns::Name& name,
+ const DomainTreeNode<T>** node,
+ DomainTreeNodeChain<T>& node_path,
+ bool (*callback)(const DomainTreeNode<T>&, CBARG),
+ CBARG callback_arg) const
+ {
+ DomainTreeNode<T>* target_node = NULL;
+ const isc::dns::LabelSequence ls(name);
+ Result ret = find(ls, &target_node, node_path, callback,
+ callback_arg);
+ if (ret != NOTFOUND) {
+ *node = target_node;
+ }
+ return (ret);
+ }
+
+ /// \brief Find with callback and node chain
+ /// \anchor callback
+ ///
+ /// This version of \c find() is specifically designed for the backend
+ /// of the \c InMemoryZoneFinder class, and implements all necessary
+ /// features for that purpose. Other applications shouldn't need these
+ /// additional features, and should normally use the simpler versions.
+ ///
+ /// This version of \c find() calls the callback whenever traversing (on
+ /// the way from root down the tree) a marked node on the way down through
+ /// the domain namespace (see \c DomainTreeNode::FLAG_CALLBACK).
+ ///
+ /// Also, this version takes a \c LabelSequence object, not a \c Name
+ /// object to be as efficient as possible; operations on the former
+ /// needed for the search are generally much more efficient than those
+ /// for the latter. Since \c Name objects are more commonly used
+ /// in other parts of the implementation, other versions take a \c Name
+ /// and convert it to \c LabelSequence. This conversion is cheap,
+ /// while the other direction isn't, and since there would be cases
+ /// where an implementation primarily handles \c LabelSequence objects
+ /// as an efficient representation of names, it would make most sense
+ /// to provide the interface that takes \c LabelSequence.
+ ///
+ /// If you return true from the callback, the search is stopped and a
+ /// PARTIALMATCH is returned with the given node. Note that this node
+ /// doesn't really need to be the one with longest possible match.
+ ///
+ /// The callback is not called for the node which matches exactly
+ /// (EXACTMATCH is returned). This is typically the last node in the
+ /// traversal during a successful search.
+ ///
+ /// This callback mechanism was designed with zone cut (delegation)
+ /// processing in mind. The marked nodes would be the ones at delegation
+ /// points. It is not expected that any other applications would need
+ /// callbacks; they should use the versions of find without callbacks.
+ /// The callbacks are not general functors for the same reason - we don't
+ /// expect it to be needed.
+ ///
+ /// Another special feature of this version is the ability to record
+ /// more detailed information regarding the search result.
+ ///
+ /// This information will be returned via the \c node_path parameter,
+ /// which is an object of class \c DomainTreeNodeChain.
+ /// The passed parameter must be empty.
+ ///
+ /// On success, the node sequence stored in \c node_path will contain all
+ /// the ancestor nodes from the found node towards the root.
+ /// For example, if we look for o.w.y.d.e.f in the example \ref diagram,
+ /// \c node_path will contain w.y and d.e.f; the \c top() node of the
+ /// chain will be o, w.y and d.e.f will be stored below it.
+ ///
+ /// This feature can be used to get the absolute name for a node;
+ /// to do so, we need to travel upside from the node toward the root,
+ /// concatenating all ancestor labels. A node chain can also be used to
+ /// find the next and previous nodes of a given node in the entire DomainTree;
+ /// the \c nextNode() and \c previousNode() methods take a node
+ /// chain as a parameter.
+ ///
+ /// \exception isc::BadValue node_path is not empty.
+ ///
+ /// \param target_labels_orig Target to be found
+ /// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
+ /// it will store a pointer to the matching node
+ /// \param node_path Other search details will be stored (see the
+ /// description)
+ /// \param callback If non- \c NULL, a call back function to be called
+ /// at marked nodes (see the description).
+ /// \param callback_arg A caller supplied argument to be passed to
+ /// \c callback.
+ ///
+ /// \return As in the description, but in case of callback returning
+ /// \c true, it returns immediately with the current node.
+ template <typename CBARG>
+ Result find(const isc::dns::LabelSequence& target_labels_orig,
+ DomainTreeNode<T>** node,
+ DomainTreeNodeChain<T>& node_path,
+ bool (*callback)(const DomainTreeNode<T>&, CBARG),
+ CBARG callback_arg) const;
+
+ /// \brief Simple find returning immutable node.
+ ///
+ /// Acts as described in the \ref find section, but returns immutable
+ /// node pointer.
+ template <typename CBARG>
+ Result find(const isc::dns::LabelSequence& target_labels,
+ const DomainTreeNode<T>** node,
+ DomainTreeNodeChain<T>& node_path,
+ bool (*callback)(const DomainTreeNode<T>&, CBARG),
+ CBARG callback_arg) const
+ {
+ DomainTreeNode<T>* target_node = NULL;
+ Result ret = find(target_labels, &target_node, node_path,
+ callback, callback_arg);
+ if (ret != NOTFOUND) {
+ *node = target_node;
+ }
+ return (ret);
+ }
+ //@}
+
+ /// \brief return the next bigger node in DNSSEC order from a given node
+ /// chain.
+ ///
+ /// This method identifies the next bigger node of the node currently
+ /// referenced in \c node_path and returns it.
+ /// This method also updates the passed \c node_path so that it will store
+ /// the path for the returned next node.
+ /// It will be convenient when we want to iterate over the all nodes
+ /// of \c DomainTree; we can do this by calling this method repeatedly
+ /// starting from the root node.
+ ///
+ /// \note \c nextNode() will iterate over all the nodes in DomainTree including
+ /// empty nodes. If empty node isn't desired, it's easy to add logic to
+ /// check return node and keep invoking \c nextNode() until the non-empty
+ /// node is retrieved.
+ ///
+ /// \exception isc::BadValue node_path is empty.
+ ///
+ /// \param node_path A node chain that stores all the nodes along the path
+ /// from root to node.
+ ///
+ /// \return An \c DomainTreeNode that is next bigger than \c node; if \c node is
+ /// the largest, \c NULL will be returned.
+ const DomainTreeNode<T>* nextNode(DomainTreeNodeChain<T>& node_path) const;
+
+ /// \brief return the next smaller node in DNSSEC order from a node
+ /// searched by DomainTree::find().
+ ///
+ /// This acts similarly to \c nextNode(), but it walks in the other
+ /// direction. But unlike \c nextNode(), this can start even if the
+ /// node requested by \c find() was not found. In that case, it will
+ /// identify the node that is previous to the queried name.
+ ///
+ /// \note \c previousNode() will iterate over all the nodes in DomainTree
+ /// including empty nodes. If empty node isn't desired, it's easy to add
+ /// logic to check return node and keep invoking \c previousNode() until the
+ /// non-empty node is retrieved.
+ ///
+ /// \exception isc::BadValue node_path is empty.
+ ///
+ /// \param node_path A node chain that stores all the nodes along the path
+ /// from root to node and the result of \c find(). This will get modified.
+ /// You should not use the node_path again except for repetitive calls
+ /// of this method.
+ ///
+ /// \return An \c DomainTreeNode that is next smaller than \c node; if \c node is
+ /// the smallest, \c NULL will be returned.
+ const DomainTreeNode<T>* previousNode(DomainTreeNodeChain<T>& node_path) const;
+
+ /// \brief Get the total number of nodes in the tree
+ ///
+ /// It includes nodes internally created as a result of adding a domain
+ /// name that is a subdomain of an existing node of the tree.
+ /// This function is mainly intended to be used for debugging.
+ int getNodeCount() const { return (node_count_); }
+
+ /// \name Debug function
+ //@{
+ /// \brief Print the nodes in the trees.
+ ///
+ /// \param os A \c std::ostream object to which the tree is printed.
+ /// \param depth A factor of the initial indentation. Each line
+ /// will begin with space character repeating <code>5 * depth</code>
+ /// times.
+ void dumpTree(std::ostream& os, unsigned int depth = 0) const;
+
+ /// \brief Print the nodes in the trees for processing with
+ /// Graphviz's dot.
+ ///
+ /// \param os A \c std::ostream object to which the tree is printed.
+ /// \param show_pointers Show node and parent pointers in the node
+ void dumpDot(std::ostream& os, bool show_pointers = false) const;
+ //@}
+
+ /// \name Modify functions
+ //@{
+ /// \brief Insert the domain name into the tree.
+ ///
+ /// It either finds an already existing node of the given name, or inserts
+ /// a new one if none exists yet. In any case, the \c inserted_node parameter
+ /// is set to point to that node. You can fill data into it or modify it.
+ /// So, if you don't know if a node exists or not and you need to modify
+ /// it, just call insert and act by the result.
+ ///
+ /// Please note that the tree can add some empty nodes by itself, so don't
+ /// assume that if you didn't insert a node of that name it doesn't exist.
+ ///
+ /// This method normally involves resource allocation. If it fails
+ /// the corresponding standard exception will be thrown.
+ ///
+ /// This method does not provide the strong exception guarantee in its
+ /// strict sense; if an exception is thrown in the middle of this
+ /// method, the internal structure may change. However, it should
+ /// still retain the same property as a mapping container before this
+ /// method is called. For example, the result of \c find() should be
+ /// the same. This method provides the weak exception guarantee in its
+ /// normal sense.
+ ///
+ /// \param mem_sgmt A \c MemorySegment object for allocating memory of
+ /// a new node to be inserted. Must be the same segment as that used
+ /// for creating the tree itself.
+ /// \param name The name to be inserted into the tree.
+ /// \param inserted_node This is an output parameter and is set to the
+ /// node.
+ ///
+ /// \return
+ /// - SUCCESS The node was added.
+ /// - ALREADYEXISTS There was already a node of that name, so it was not
+ /// added.
+ Result insert(util::MemorySegment& mem_sgmt, const isc::dns::Name& name,
+ DomainTreeNode<T>** inserted_node);
+
+ /// \brief Delete all tree nodes.
+ ///
+ /// \throw none.
+ ///
+ /// \param mem_sgmt The \c MemorySegment object used to insert the nodes
+ /// (which was also used for creating the tree due to the requirement of
+ /// \c inert()).
+ void deleteAllNodes(util::MemorySegment& mem_sgmt);
+
+ /// \brief Swaps two tree's contents.
+ ///
+ /// This and \c other trees must have been created with the same
+ /// memory segment (see the discussion in \c create()); otherwise the
+ /// behavior is undefined.
+ ///
+ /// This acts the same as many std::*.swap functions, exchanges the
+ /// contents. This doesn't throw anything.
+ void swap(DomainTree<T>& other) {
+ std::swap(root_, other.root_);
+ std::swap(node_count_, other.node_count_);
+ }
+ //@}
+
+private:
+ /// \name DomainTree balance functions
+ //@{
+ void insertRebalance(typename DomainTreeNode<T>::DomainTreeNodePtr* root, DomainTreeNode<T>* node);
+ DomainTreeNode<T>* rightRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+ DomainTreeNode<T>* node);
+ DomainTreeNode<T>* leftRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+ DomainTreeNode<T>* node);
+ //@}
+
+ /// \name Helper functions
+ //@{
+ /// \brief delete tree whose root is equal to node
+ void deleteHelper(util::MemorySegment& mem_sgmt, DomainTreeNode<T> *node);
+
+ /// \brief Print the information of given DomainTreeNode.
+ void dumpTreeHelper(std::ostream& os, const DomainTreeNode<T>* node,
+ unsigned int depth) const;
+
+ /// \brief Print the information of given DomainTreeNode for dot.
+ int dumpDotHelper(std::ostream& os, const DomainTreeNode<T>* node,
+ int* nodecount, bool show_pointers) const;
+
+ /// \brief Indentation helper function for dumpTree
+ static void indent(std::ostream& os, unsigned int depth);
+
+ /// Split one node into two nodes for "prefix" and "suffix" parts of
+ /// the labels of the original node, respectively. The given node
+ /// will hold the suffix labels, while the new node will hold the prefix.
+ /// The newly created node represents the labels that the original node
+ /// did, so necessary data are swapped.
+ /// (Note: as commented in the code, this behavior should be changed).
+ void nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T>& node,
+ const isc::dns::LabelSequence& new_prefix,
+ const isc::dns::LabelSequence& new_suffix);
+ //@}
+
+ typename DomainTreeNode<T>::DomainTreeNodePtr root_;
+ /// the node count of current tree
+ unsigned int node_count_;
+ /// search policy for rbtree
+ const bool needsReturnEmptyNode_;
+};
+
+template <typename T>
+DomainTree<T>::DomainTree(bool returnEmptyNode) :
+ root_(NULL),
+ node_count_(0),
+ needsReturnEmptyNode_(returnEmptyNode)
+{
+}
+
+template <typename T>
+DomainTree<T>::~DomainTree() {
+ assert(node_count_ == 0);
+}
+
+template <typename T>
+void
+DomainTree<T>::deleteHelper(util::MemorySegment& mem_sgmt, DomainTreeNode<T>* root) {
+ if (root == NULL) {
+ return;
+ }
+
+ DomainTreeNode<T>* node = root;
+ while (root->getLeft() != NULL || root->getRight() != NULL) {
+ DomainTreeNode<T>* left(NULL);
+ DomainTreeNode<T>* right(NULL);
+ while ((left = node->getLeft()) != NULL ||
+ (right = node->getRight()) != NULL) {
+ node = (left != NULL) ? left : right;
+ }
+
+ DomainTreeNode<T>* parent = node->getParent();
+ if (parent->getLeft() == node) {
+ parent->left_ = NULL;
+ } else {
+ parent->right_ = NULL;
+ }
+
+ deleteHelper(mem_sgmt, node->getDown());
+ DomainTreeNode<T>::destroy(mem_sgmt, node);
+ --node_count_;
+ node = parent;
+ }
+
+ deleteHelper(mem_sgmt, root->getDown());
+ DomainTreeNode<T>::destroy(mem_sgmt, root);
+ --node_count_;
+}
+
+template <typename T>
+template <typename CBARG>
+typename DomainTree<T>::Result
+DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
+ DomainTreeNode<T>** target,
+ DomainTreeNodeChain<T>& node_path,
+ bool (*callback)(const DomainTreeNode<T>&, CBARG),
+ CBARG callback_arg) const
+{
+ if (!node_path.isEmpty()) {
+ isc_throw(isc::BadValue, "DomainTree::find is given a non empty chain");
+ }
+
+ DomainTreeNode<T>* node = root_.get();
+ Result ret = NOTFOUND;
+ dns::LabelSequence target_labels(target_labels_orig);
+
+ while (node != NULL) {
+ node_path.last_compared_ = node;
+ node_path.last_comparison_ = target_labels.compare(node->getLabels());
+ const isc::dns::NameComparisonResult::NameRelation relation =
+ node_path.last_comparison_.getRelation();
+
+ if (relation == isc::dns::NameComparisonResult::EQUAL) {
+ if (needsReturnEmptyNode_ || !node->isEmpty()) {
+ node_path.push(node);
+ *target = node;
+ ret = EXACTMATCH;
+ }
+ break;
+ } else if (relation == isc::dns::NameComparisonResult::NONE) {
+ // If the two labels have no hierarchical relationship in terms
+ // of matching, we should continue the binary search.
+ node = (node_path.last_comparison_.getOrder() < 0) ?
+ node->getLeft() : node->getRight();
+ } else {
+ if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
+ if (needsReturnEmptyNode_ || !node->isEmpty()) {
+ ret = PARTIALMATCH;
+ *target = node;
+ if (callback != NULL &&
+ node->getFlag(DomainTreeNode<T>::FLAG_CALLBACK)) {
+ if ((callback)(*node, callback_arg)) {
+ break;
+ }
+ }
+ }
+ node_path.push(node);
+ target_labels.stripRight(
+ node_path.last_comparison_.getCommonLabels());
+ node = node->getDown();
+ } else {
+ break;
+ }
+ }
+ }
+
+ return (ret);
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::nextNode(DomainTreeNodeChain<T>& node_path) const {
+ if (node_path.isEmpty()) {
+ isc_throw(isc::BadValue, "DomainTree::nextNode is given an empty chain");
+ }
+
+ const DomainTreeNode<T>* node = node_path.top();
+ // if node has sub domain, the next domain is the smallest
+ // domain in sub domain tree
+ const DomainTreeNode<T>* down = node->getDown();
+ if (down != NULL) {
+ const DomainTreeNode<T>* left_most = down;
+ while (left_most->getLeft() != NULL) {
+ left_most = left_most->getLeft();
+ }
+ node_path.push(left_most);
+ return (left_most);
+ }
+
+ // try to find a successor.
+ // if no successor found move to up level, the next successor
+ // is the successor of up node in the up level tree, if
+ // up node doesn't have successor we gonna keep moving to up
+ // level
+ while (!node_path.isEmpty()) {
+ const DomainTreeNode<T>* up_node_successor = node_path.top()->successor();
+ node_path.pop();
+ if (up_node_successor != NULL) {
+ node_path.push(up_node_successor);
+ return (up_node_successor);
+ }
+ }
+
+ return (NULL);
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
+ if (getNodeCount() == 0) {
+ // Special case for empty trees. It would look every time like
+ // we didn't search, because the last compared is empty. This is
+ // a slight hack and not perfect, but this is better than throwing
+ // on empty tree. And we probably won't meet an empty tree in practice
+ // anyway.
+ return (NULL);
+ }
+ if (node_path.last_compared_ == NULL) {
+ isc_throw(isc::BadValue,
+ "DomainTree::previousNode() called before find()");
+ }
+
+ // If the relation isn't EQUAL, it means the find was called previously
+ // and didn't find the exact node. Therefore we need to locate the place
+ // to start iterating the chain of domains.
+ //
+ // The logic here is not too complex, we just need to take care to handle
+ // all the cases and decide where to go from there.
+ switch (node_path.last_comparison_.getRelation()) {
+ case dns::NameComparisonResult::COMMONANCESTOR:
+ case dns::NameComparisonResult::NONE:
+ // We compared with a leaf in the tree and wanted to go to one of
+ // the children. But the child was not there. It now depends on the
+ // direction in which we wanted to go.
+ if (node_path.last_comparison_.getOrder() < 0) {
+ // We wanted to go left. So the one we compared with is
+ // the one higher than we wanted. If we just put it into
+ // the node_path, then the following algorithm below will find
+ // the smaller one.
+ //
+ // This is exactly the same as with superdomain below.
+ // Therefore, we just fall through to the next case.
+ } else {
+ // We wanted to go right. That means we want to output the
+ // one which is the largest in the tree defined by the
+ // compared one (it is either the compared one, or some
+ // subdomain of it). There probably is not an easy trick
+ // for this, so we just find the correct place.
+ const DomainTreeNode<T>* current(node_path.last_compared_);
+ while (current != NULL) {
+ node_path.push(current);
+ // Go a level down and as much right there as possible
+ current = current->getDown();
+ if (current != NULL) {
+ const DomainTreeNode<T>* right;
+ while ((right = current->getRight()) != NULL) {
+ current = right;
+ }
+ }
+ }
+ // Now, the one on top of the path is the one we want. We
+ // return it now and leave it there, so we can search for
+ // previous of it the next time we'are called.
+ node_path.last_comparison_ =
+ dns::NameComparisonResult(0, 0,
+ dns::NameComparisonResult::EQUAL);
+ return (node_path.top());
+ }
+ // No break; here - we want to fall through. See above.
+ case dns::NameComparisonResult::SUPERDOMAIN:
+ // This is the case there's a "compressed" node and we looked for
+ // only part of it. The node itself is larger than we wanted, but
+ // if we put it to the node_path and then go one step left from it,
+ // we get the correct result.
+ node_path.push(node_path.last_compared_);
+ // Correct the comparison result, so we won't trigger this case
+ // next time previousNode is called. We already located the correct
+ // place to start. The value is partly nonsense, but that doesn't
+ // matter any more.
+ node_path.last_comparison_ =
+ dns::NameComparisonResult(0, 0,
+ dns::NameComparisonResult::EQUAL);
+ break;
+ case dns::NameComparisonResult::SUBDOMAIN:
+ // A subdomain means we returned the one above the searched one
+ // already and it is on top of the stack. This is was smaller
+ // than the one already, but we want to return yet smaller one.
+ // So we act as if it was EQUAL.
+ break;
+ case dns::NameComparisonResult::EQUAL:
+ // The find gave us an exact match or the previousNode was called
+ // already, which located the exact node. The rest of the function
+ // goes one domain left and returns it for us.
+ break;
+ }
+
+ // So, the node_path now contains the path to a node we want previous for.
+ // We just need to go one step left.
+
+ if (node_path.isEmpty()) {
+ // We got past the first one. So, we're returning NULL from
+ // now on.
+ return (NULL);
+ }
+
+ const DomainTreeNode<T>* node(node_path.top());
+
+ // Try going left in this tree
+ node = node->predecessor();
+ if (node == NULL) {
+ // We are the smallest ones in this tree. We go one level
+ // up. That one is the smaller one than us.
+
+ node_path.pop();
+ if (node_path.isEmpty()) {
+ // We're past the first one
+ return (NULL);
+ } else {
+ return (node_path.top());
+ }
+ }
+
+ // Exchange the node at the top of the path, as we move horizontaly
+ // through the domain tree
+ node_path.pop();
+ node_path.push(node);
+
+ // Try going as deep as possible, keeping on the right side of the trees
+ const DomainTreeNode<T>* down;
+ while ((down = node->getDown()) != NULL) {
+ // Move to the tree below
+ node = down;
+ if (node != NULL) {
+ // And get as much to the right of the tree as possible
+ const DomainTreeNode<T>* right;
+ while ((right = node->getRight()) != NULL) {
+ node = right;
+ }
+ }
+ // Now, we found the right-most node in the sub-tree, we need to
+ // include it in the path
+ node_path.push(node);
+ }
+
+ // Now, if the current node has no down_ pointer any more, it's the
+ // correct one.
+ return (node);
+}
+
+template <typename T>
+typename DomainTree<T>::Result
+DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
+ const isc::dns::Name& target_name, DomainTreeNode<T>** new_node)
+{
+ DomainTreeNode<T>* parent = NULL;
+ DomainTreeNode<T>* current = root_.get();
+ DomainTreeNode<T>* up_node = NULL;
+ isc::dns::LabelSequence target_labels(target_name);
+
+ int order = -1;
+ while (current != NULL) {
+ const dns::LabelSequence current_labels(current->getLabels());
+ const isc::dns::NameComparisonResult compare_result =
+ target_labels.compare(current_labels);
+ const isc::dns::NameComparisonResult::NameRelation relation =
+ compare_result.getRelation();
+ if (relation == isc::dns::NameComparisonResult::EQUAL) {
+ if (new_node != NULL) {
+ *new_node = current;
+ }
+ return (ALREADYEXISTS);
+ } else if (relation == isc::dns::NameComparisonResult::NONE) {
+ parent = current;
+ order = compare_result.getOrder();
+ current = order < 0 ? current->getLeft() : current->getRight();
+ } else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
+ // insert sub domain to sub tree
+ parent = NULL;
+ up_node = current;
+ target_labels.stripRight(compare_result.getCommonLabels());
+ current = current->getDown();
+ } else {
+ // The number of labels in common is fewer than the number of
+ // labels at the current node, so the current node must be
+ // adjusted to have just the common suffix, and a down pointer
+ // made to a new tree.
+ dns::LabelSequence common_ancestor = target_labels;
+ common_ancestor.stripLeft(target_labels.getLabelCount() -
+ compare_result.getCommonLabels());
+ dns::LabelSequence new_prefix = current_labels;
+ new_prefix.stripRight(compare_result.getCommonLabels());
+ nodeFission(mem_sgmt, *current, new_prefix, common_ancestor);
+ }
+ }
+
+ typename DomainTreeNode<T>::DomainTreeNodePtr* current_root = (up_node != NULL) ?
+ &(up_node->down_) : &root_;
+ // Once a new node is created, no exception will be thrown until the end
+ // of the function, so we can simply create and hold a new node pointer.
+ DomainTreeNode<T>* node = DomainTreeNode<T>::create(mem_sgmt, target_labels);
+ node->parent_ = parent;
+ if (parent == NULL) {
+ *current_root = node;
+ // node is the new root of sub tree, so its init color is BLACK
+ node->setColor(DomainTreeNode<T>::BLACK);
+ node->setSubTreeRoot(true);
+ node->parent_ = up_node;
+ } else if (order < 0) {
+ node->setSubTreeRoot(false);
+ parent->left_ = node;
+ } else {
+ node->setSubTreeRoot(false);
+ parent->right_ = node;
+ }
+ insertRebalance(current_root, node);
+ if (new_node != NULL) {
+ *new_node = node;
+ }
+
+ ++node_count_;
+ return (SUCCESS);
+}
+
+template <typename T>
+void
+DomainTree<T>::deleteAllNodes(util::MemorySegment& mem_sgmt) {
+ deleteHelper(mem_sgmt, root_.get());
+ root_ = NULL;
+}
+
+// Note: when we redesign this (still keeping the basic concept), we should
+// change this part so the newly created node will be used for the inserted
+// name (and therefore the name for the existing node doesn't change).
+// Otherwise, things like shortcut links between nodes won't work.
+// See Trac #2054.
+template <typename T>
+void
+DomainTree<T>::nodeFission(util::MemorySegment& mem_sgmt, DomainTreeNode<T>& node,
+ const isc::dns::LabelSequence& new_prefix,
+ const isc::dns::LabelSequence& new_suffix)
+{
+ // Create and reset the labels.
+ // Once a new node is created, no exception will be thrown until
+ // the end of the function, and it will keep consistent behavior
+ // (i.e., a weak form of strong exception guarantee) even if code
+ // after the call to this function throws an exception.
+ DomainTreeNode<T>* down_node = DomainTreeNode<T>::create(mem_sgmt, new_prefix);
+ node.resetLabels(new_suffix);
+
+ std::swap(node.data_, down_node->data_);
+
+ // Swap flags bitfields; yes, this is ugly (it appears we cannot use
+ // std::swap for bitfields). The right solution is to implement
+ // the above note regarding #2054, then we won't have to swap the
+ // flags in the first place.
+ const bool is_root = node.isSubTreeRoot();
+ const uint32_t tmp = node.flags_;
+ node.flags_ = down_node->flags_;
+ down_node->flags_ = tmp;
+ node.setSubTreeRoot(is_root);
+
+ down_node->down_ = node.getDown();
+ if (down_node->down_ != NULL) {
+ down_node->down_->parent_ = down_node;
+ }
+
+ node.down_ = down_node;
+ down_node->parent_ = &node;
+
+ // Restore the color of the node (may have gotten changed by the flags
+ // swap)
+ node.setColor(down_node->getColor());
+
+ // root node of sub tree, the initial color is BLACK
+ down_node->setColor(DomainTreeNode<T>::BLACK);
+
+ // mark it as the root of a subtree
+ down_node->setSubTreeRoot(true);
+
+ ++node_count_;
+}
+
+
+template <typename T>
+void
+DomainTree<T>::insertRebalance(typename DomainTreeNode<T>::DomainTreeNodePtr* root,
+ DomainTreeNode<T>* node)
+{
+ DomainTreeNode<T>* uncle;
+ DomainTreeNode<T>* parent;
+ while (node != (*root).get() &&
+ (parent = node->getParent())->getColor() == DomainTreeNode<T>::RED) {
+ // Here, node->parent_ is not NULL and it is also red, so
+ // node->parent_->parent_ is also not NULL.
+ if (parent == parent->getParent()->getLeft()) {
+ uncle = parent->getParent()->getRight();
+
+ if (uncle != NULL && uncle->getColor() == DomainTreeNode<T>::RED) {
+ parent->setColor(DomainTreeNode<T>::BLACK);
+ uncle->setColor(DomainTreeNode<T>::BLACK);
+ parent->getParent()->setColor(DomainTreeNode<T>::RED);
+ node = parent->getParent();
+ } else {
+ if (node == parent->getRight()) {
+ node = parent;
+ leftRotate(root, node);
+ parent = node->getParent();
+ }
+ parent->setColor(DomainTreeNode<T>::BLACK);
+ parent->getParent()->setColor(DomainTreeNode<T>::RED);
+ rightRotate(root, parent->getParent());
+ }
+ } else {
+ uncle = parent->getParent()->getLeft();
+ if (uncle != NULL && uncle->getColor() == DomainTreeNode<T>::RED) {
+ parent->setColor(DomainTreeNode<T>::BLACK);
+ uncle->setColor(DomainTreeNode<T>::BLACK);
+ parent->getParent()->setColor(DomainTreeNode<T>::RED);
+ node = parent->getParent();
+ } else {
+ if (node == parent->getLeft()) {
+ node = parent;
+ rightRotate(root, node);
+ parent = node->getParent();
+ }
+ parent->setColor(DomainTreeNode<T>::BLACK);
+ parent->getParent()->setColor(DomainTreeNode<T>::RED);
+ leftRotate(root, parent->getParent());
+ }
+ }
+ }
+
+ (*root)->setColor(DomainTreeNode<T>::BLACK);
+}
+
+
+template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::leftRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root, DomainTreeNode<T>* node) {
+ DomainTreeNode<T>* const right = node->getRight();
+ DomainTreeNode<T>* const rleft = right->getLeft();
+ node->right_ = rleft;
+ if (rleft != NULL) {
+ rleft->parent_ = node;
+ }
+
+ DomainTreeNode<T>* const parent = node->getParent();
+ right->parent_ = parent;
+
+ if (!node->isSubTreeRoot()) {
+ right->setSubTreeRoot(false);
+ if (node == parent->getLeft()) {
+ parent->left_ = right;
+ } else {
+ parent->right_ = right;
+ }
+ } else {
+ right->setSubTreeRoot(true);
+ *root = right;
+ }
+
+ right->left_ = node;
+ node->parent_ = right;
+ node->setSubTreeRoot(false);
+ return (node);
+}
+
+template <typename T>
+DomainTreeNode<T>*
+DomainTree<T>::rightRotate(typename DomainTreeNode<T>::DomainTreeNodePtr* root, DomainTreeNode<T>* node) {
+ DomainTreeNode<T>* const left = node->getLeft();
+ DomainTreeNode<T>* const lright = left->getRight();
+ node->left_ = lright;
+ if (lright != NULL) {
+ lright->parent_ = node;
+ }
+
+ DomainTreeNode<T>* const parent = node->getParent();
+ left->parent_ = parent;
+
+ if (!node->isSubTreeRoot()) {
+ left->setSubTreeRoot(false);
+ if (node == parent->getRight()) {
+ parent->right_ = left;
+ } else {
+ parent->left_ = left;
+ }
+ } else {
+ left->setSubTreeRoot(true);
+ *root = left;
+ }
+ left->right_ = node;
+ node->parent_ = left;
+ node->setSubTreeRoot(false);
+
+ return (node);
+}
+
+
+template <typename T>
+void
+DomainTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
+ indent(os, depth);
+ os << "tree has " << node_count_ << " node(s)\n";
+ dumpTreeHelper(os, root_.get(), depth);
+}
+
+template <typename T>
+void
+DomainTree<T>::dumpTreeHelper(std::ostream& os, const DomainTreeNode<T>* node,
+ unsigned int depth) const
+{
+ if (node == NULL) {
+ indent(os, depth);
+ os << "NULL\n";
+ return;
+ }
+
+ indent(os, depth);
+ os << node->getLabels() << " ("
+ << ((node->getColor() == DomainTreeNode<T>::BLACK) ? "black" : "red")
+ << ")";
+ if (node->isEmpty()) {
+ os << " [invisible]";
+ }
+ if (node->isSubTreeRoot()) {
+ os << " [subtreeroot]";
+ }
+ os << "\n";
+
+ const DomainTreeNode<T>* down = node->getDown();
+ if (down != NULL) {
+ indent(os, depth + 1);
+ os << "begin down from " << node->getLabels() << "\n";
+ dumpTreeHelper(os, down, depth + 1);
+ indent(os, depth + 1);
+ os << "end down from " << node->getLabels() << "\n";
+ }
+ dumpTreeHelper(os, node->getLeft(), depth + 1);
+ dumpTreeHelper(os, node->getRight(), depth + 1);
+}
+
+template <typename T>
+void
+DomainTree<T>::indent(std::ostream& os, unsigned int depth) {
+ static const unsigned int INDENT_FOR_EACH_DEPTH = 5;
+ os << std::string(depth * INDENT_FOR_EACH_DEPTH, ' ');
+}
+
+template <typename T>
+void
+DomainTree<T>::dumpDot(std::ostream& os, bool show_pointers) const {
+ int nodecount = 0;
+
+ os << "digraph g {\n";
+ os << "node [shape = record,height=.1];\n";
+ dumpDotHelper(os, root_.get(), &nodecount, show_pointers);
+ os << "}\n";
+}
+
+template <typename T>
+int
+DomainTree<T>::dumpDotHelper(std::ostream& os, const DomainTreeNode<T>* node,
+ int* nodecount, bool show_pointers) const
+{
+ if (node == NULL) {
+ return 0;
+ }
+
+ int l = dumpDotHelper(os, node->getLeft(), nodecount, show_pointers);
+ int r = dumpDotHelper(os, node->getRight(), nodecount, show_pointers);
+ int d = dumpDotHelper(os, node->getDown(), nodecount, show_pointers);
+
+ *nodecount += 1;
+
+ os << "node" << *nodecount <<
+ "[label = \"<f0> |<f1> " << node->getLabels() <<
+ "|<f2>";
+ if (show_pointers) {
+ os << "|<f3> n=" << node << "|<f4> p=" << node->getParent();
+ }
+ os << "\"] [";
+
+ if (node->getColor() == DomainTreeNode<T>::RED) {
+ os << "color=red";
+ } else {
+ os << "color=black";
+ }
+
+ if (node->isSubTreeRoot()) {
+ os << ",penwidth=3";
+ }
+
+ if (node->isEmpty()) {
+ os << ",style=filled,fillcolor=lightgrey";
+ }
+
+ os << "];\n";
+
+ if (node->getLeft() != NULL) {
+ os << "\"node" << *nodecount << "\":f0 -> \"node" << l << "\":f1;\n";
+ }
+
+ if (node->getDown() != NULL) {
+ os << "\"node" << *nodecount << "\":f1 -> \"node" << d << "\":f1 [penwidth=5];\n";
+ }
+
+ if (node->getRight() != NULL) {
+ os << "\"node" << *nodecount << "\":f2 -> \"node" << r << "\":f1;\n";
+ }
+
+ return (*nodecount);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // _DOMAINTREE_H
+
+// Local Variables:
+// mode: c++
+// End:
--- /dev/null
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/domaintree.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+using isc::UnitTestUtil;
+using namespace isc::datasrc::memory;
+
+// XXX: some compilers cannot find class static constants used in
+// EXPECT_xxx macros, for which we need an explicit empty definition.
+const size_t Name::MAX_LABELS;
+
+/* The initial structure of rbtree
+ *
+* .
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+
+namespace {
+class TreeHolder {
+public:
+ TreeHolder(util::MemorySegment& mem_sgmt, DomainTree<int>* tree) :
+ mem_sgmt_(mem_sgmt), tree_(tree)
+ {}
+ ~TreeHolder() {
+ DomainTree<int>::destroy(mem_sgmt_, tree_);
+ }
+ DomainTree<int>* get() { return (tree_); }
+private:
+ util::MemorySegment& mem_sgmt_;
+ DomainTree<int>* tree_;
+};
+
+class DomainTreeTest : public::testing::Test {
+protected:
+ DomainTreeTest() :
+ rbtree_holder_(mem_sgmt_, DomainTree<int>::create(mem_sgmt_)),
+ rbtree_expose_empty_node_holder_(mem_sgmt_,
+ DomainTree<int>::create(mem_sgmt_, true)),
+ rbtree(*rbtree_holder_.get()),
+ rbtree_expose_empty_node(*rbtree_expose_empty_node_holder_.get()),
+ crbtnode(NULL)
+ {
+ const char* const domain_names[] = {
+ "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+ int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+ for (int i = 0; i < name_count; ++i) {
+ rbtree.insert(mem_sgmt_, Name(domain_names[i]), &rbtnode);
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(i + 1)));
+
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
+ &rbtnode);
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(i + 1)));
+
+ }
+ }
+
+ util::MemorySegmentLocal mem_sgmt_;
+ TreeHolder rbtree_holder_;
+ TreeHolder rbtree_expose_empty_node_holder_;
+ DomainTree<int>& rbtree;
+ DomainTree<int>& rbtree_expose_empty_node;
+ DomainTreeNode<int>* rbtnode;
+ const DomainTreeNode<int>* crbtnode;
+};
+
+TEST_F(DomainTreeTest, nodeCount) {
+ EXPECT_EQ(15, rbtree.getNodeCount());
+
+ // Delete all nodes, then the count should be set to 0. This also tests
+ // the behavior of deleteAllNodes().
+ rbtree.deleteAllNodes(mem_sgmt_);
+ EXPECT_EQ(0, rbtree.getNodeCount());
+}
+
+TEST_F(DomainTreeTest, setGetData) {
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(11)));
+ EXPECT_EQ(11, *(rbtnode->getData()));
+}
+
+TEST_F(DomainTreeTest, insertNames) {
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_,
+ Name("d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(Name("d.e.f"), rbtnode->getName());
+ EXPECT_EQ(15, rbtree.getNodeCount());
+
+ // insert not exist node
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("0"),
+ &rbtnode));
+ EXPECT_EQ(Name("0"), rbtnode->getName());
+ EXPECT_EQ(16, rbtree.getNodeCount());
+
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
+ Name("example.com"),
+ &rbtnode));
+ EXPECT_EQ(17, rbtree.getNodeCount());
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(12)));
+
+ // return ALREADYEXISTS, since node "example.com" already has
+ // been explicitly inserted
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_,
+ Name("example.com"),
+ &rbtnode));
+ EXPECT_EQ(17, rbtree.getNodeCount());
+
+ // split the node "d.e.f"
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("k.e.f"),
+ &rbtnode));
+ EXPECT_EQ(Name("k"), rbtnode->getName());
+ EXPECT_EQ(19, rbtree.getNodeCount());
+
+ // split the node "g.h"
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_, Name("h"),
+ &rbtnode));
+ EXPECT_EQ(Name("h"), rbtnode->getName());
+ EXPECT_EQ(20, rbtree.getNodeCount());
+
+ // add child domain
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
+ Name("m.p.w.y.d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(Name("m"), rbtnode->getName());
+ EXPECT_EQ(21, rbtree.getNodeCount());
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
+ Name("n.p.w.y.d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(Name("n"), rbtnode->getName());
+ EXPECT_EQ(22, rbtree.getNodeCount());
+
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("l.a"),
+ &rbtnode));
+ EXPECT_EQ(Name("l"), rbtnode->getName());
+ EXPECT_EQ(23, rbtree.getNodeCount());
+
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("r.d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("s.d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(25, rbtree.getNodeCount());
+
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
+ Name("h.w.y.d.e.f"),
+ &rbtnode));
+
+ // add more nodes one by one to cover leftRotate and rightRotate
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt_, Name("f"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("m"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("nm"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("om"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("k"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("l"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("fe"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("ge"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("i"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("ae"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_, Name("n"),
+ &rbtnode));
+}
+
+TEST_F(DomainTreeTest, subTreeRoot) {
+ // This is a testcase for a particular issue that went unchecked in
+ // #2089's implementation, but was fixed in #2092. The issue was
+ // that when a node was fissioned, FLAG_SUBTREE_ROOT was not being
+ // copied correctly.
+
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS,
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name("d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS,
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS,
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS,
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::SUCCESS,
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name("k.e.f"),
+ &rbtnode));
+
+ // "g.h" is not a subtree root
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree_expose_empty_node.find(Name("g.h"), &rbtnode));
+ EXPECT_FALSE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_SUBTREE_ROOT));
+
+ // fission the node "g.h"
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS,
+ rbtree_expose_empty_node.insert(mem_sgmt_, Name("h"),
+ &rbtnode));
+
+ // the node "h" (h.down_ -> "g") should not be a subtree root. "g"
+ // should be a subtree root.
+ EXPECT_FALSE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_SUBTREE_ROOT));
+
+ // "g.h" should be a subtree root now.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree_expose_empty_node.find(Name("g.h"), &rbtnode));
+ EXPECT_TRUE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_SUBTREE_ROOT));
+}
+
+TEST_F(DomainTreeTest, findName) {
+ // find const rbtnode
+ // exact match
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, rbtree.find(Name("a"), &crbtnode));
+ EXPECT_EQ(Name("a"), crbtnode->getName());
+
+ // not found
+ EXPECT_EQ(DomainTree<int>::NOTFOUND, rbtree.find(Name("d.e.f"), &crbtnode));
+ EXPECT_EQ(DomainTree<int>::NOTFOUND, rbtree.find(Name("y.d.e.f"), &crbtnode));
+ EXPECT_EQ(DomainTree<int>::NOTFOUND, rbtree.find(Name("x"), &crbtnode));
+ EXPECT_EQ(DomainTree<int>::NOTFOUND, rbtree.find(Name("m.n"), &crbtnode));
+
+ // if we expose empty node, we can get the empty node created during insert
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree_expose_empty_node.find(Name("d.e.f"), &crbtnode));
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree_expose_empty_node.find(Name("w.y.d.e.f"), &crbtnode));
+
+ // partial match
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH, rbtree.find(Name("m.b"), &crbtnode));
+ EXPECT_EQ(Name("b"), crbtnode->getName());
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ rbtree_expose_empty_node.find(Name("m.d.e.f"), &crbtnode));
+
+ // find rbtnode
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, rbtree.find(Name("q.w.y.d.e.f"),
+ &rbtnode));
+ EXPECT_EQ(Name("q"), rbtnode->getName());
+}
+
+TEST_F(DomainTreeTest, findError) {
+ // For the version that takes a node chain, the chain must be empty.
+ DomainTreeNodeChain<int> chain;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, rbtree.find(Name("a"), &crbtnode,
+ chain));
+ // trying to reuse the same chain. it should result in an exception.
+ EXPECT_THROW(rbtree.find(Name("a"), &crbtnode, chain),
+ BadValue);
+}
+
+TEST_F(DomainTreeTest, flags) {
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt_,
+ Name("flags.example"),
+ &rbtnode));
+
+ // by default, flags are all off
+ EXPECT_FALSE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // set operation, by default it enables the flag
+ rbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK);
+ EXPECT_TRUE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // try disable the flag explicitly
+ rbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK, false);
+ EXPECT_FALSE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // try enable the flag explicitly
+ rbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK, true);
+ EXPECT_TRUE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // setting an unknown flag will trigger an exception
+ EXPECT_THROW(rbtnode->setFlag(static_cast<DomainTreeNode<int>::Flags>(2), true),
+ isc::InvalidParameter);
+}
+
+bool
+testCallback(const DomainTreeNode<int>&, bool* callback_checker) {
+ *callback_checker = true;
+ return (false);
+}
+
+template <typename T>
+void
+performCallbackTest(DomainTree<int>& rbtree,
+ util::MemorySegmentLocal& mem_sgmt,
+ const T& name_called,
+ const T& name_not_called)
+{
+ DomainTreeNode<int>* rbtnode;
+ const DomainTreeNode<int>* crbtnode;
+
+ // by default callback isn't enabled
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt,
+ Name("callback.example"),
+ &rbtnode));
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(1)));
+ EXPECT_FALSE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // enable/re-disable callback
+ rbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK);
+ EXPECT_TRUE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+ rbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK, false);
+ EXPECT_FALSE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // enable again for subsequent tests
+ rbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK);
+ // add more levels below and above the callback node for partial match.
+ DomainTreeNode<int>* subrbtnode;
+ EXPECT_EQ(DomainTree<int>::SUCCESS, rbtree.insert(mem_sgmt,
+ Name("sub.callback.example"),
+ &subrbtnode));
+ subrbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(2)));
+ DomainTreeNode<int>* parentrbtnode;
+ EXPECT_EQ(DomainTree<int>::ALREADYEXISTS, rbtree.insert(mem_sgmt,
+ Name("example"),
+ &parentrbtnode));
+ // the child/parent nodes shouldn't "inherit" the callback flag.
+ // "rbtnode" may be invalid due to the insertion, so we need to re-find
+ // it.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
+ &rbtnode));
+ EXPECT_TRUE(rbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+ EXPECT_FALSE(subrbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+ EXPECT_FALSE(parentrbtnode->getFlag(DomainTreeNode<int>::FLAG_CALLBACK));
+
+ // check if the callback is called from find()
+ DomainTreeNodeChain<int> node_path1;
+ bool callback_called = false;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(name_called, &crbtnode, node_path1,
+ testCallback, &callback_called));
+ EXPECT_TRUE(callback_called);
+
+ // enable callback at the parent node, but it doesn't have data so
+ // the callback shouldn't be called.
+ DomainTreeNodeChain<int> node_path2;
+ parentrbtnode->setFlag(DomainTreeNode<int>::FLAG_CALLBACK);
+ callback_called = false;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(name_not_called, &crbtnode, node_path2,
+ testCallback, &callback_called));
+ EXPECT_FALSE(callback_called);
+}
+
+TEST_F(DomainTreeTest, callbackName) {
+ const Name n1("sub.callback.example");
+ const Name n2("callback.example");
+
+ performCallbackTest(rbtree, mem_sgmt_, n1, n2);
+}
+
+TEST_F(DomainTreeTest, callbackLabelSequence) {
+ const Name n1("sub.callback.example");
+ const Name n2("callback.example");
+ const LabelSequence ls1(n1);
+ const LabelSequence ls2(n2);
+
+ performCallbackTest(rbtree, mem_sgmt_, ls1, ls2);
+}
+
+TEST_F(DomainTreeTest, chainLevel) {
+ DomainTreeNodeChain<int> chain;
+
+ // by default there should be no level in the chain.
+ EXPECT_EQ(0, chain.getLevelCount());
+
+ // insert one node to the tree and find it. there should be exactly
+ // one level in the chain.
+ TreeHolder tree_holder(mem_sgmt_, DomainTree<int>::create(mem_sgmt_, true));
+ DomainTree<int>& tree(*tree_holder.get());
+ Name node_name(Name::ROOT_NAME());
+ EXPECT_EQ(DomainTree<int>::SUCCESS, tree.insert(mem_sgmt_, node_name,
+ &rbtnode));
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(node_name, &crbtnode, chain));
+ EXPECT_EQ(1, chain.getLevelCount());
+
+ /*
+ * Now creating a possibly deepest tree with MAX_LABELS levels.
+ * it should look like:
+ * (.)
+ * |
+ * a
+ * |
+ * a
+ * : (MAX_LABELS - 1) "a"'s
+ *
+ * then confirm that find() for the deepest name succeeds without any
+ * disruption, and the resulting chain has the expected level.
+ * Note that the root name (".") solely belongs to a single level,
+ * so the levels begin with 2.
+ */
+ for (unsigned int i = 2; i <= Name::MAX_LABELS; ++i) {
+ node_name = Name("a.").concatenate(node_name);
+ EXPECT_EQ(DomainTree<int>::SUCCESS, tree.insert(mem_sgmt_, node_name,
+ &rbtnode));
+ DomainTreeNodeChain<int> found_chain;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(node_name, &crbtnode, found_chain));
+ EXPECT_EQ(i, found_chain.getLevelCount());
+ }
+
+ // Confirm the last inserted name has the possible maximum length with
+ // maximum label count. This confirms the rbtree and chain level cannot
+ // be larger.
+ EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
+ EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
+}
+
+TEST_F(DomainTreeTest, getAbsoluteNameError) {
+ // an empty chain isn't allowed.
+ DomainTreeNodeChain<int> chain;
+ EXPECT_THROW(chain.getAbsoluteName(), BadValue);
+}
+
+/*
+ *the domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ * . (no data, can't be found)
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+const char* const names[] = {
+ "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+ "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+ "g.h", "i.g.h", "k.g.h"};
+const size_t name_count(sizeof(names) / sizeof(*names));
+
+const char* const upper_node_names[] = {
+ ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
+ "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
+ ".", "g.h", "g.h"};
+
+TEST_F(DomainTreeTest, getUpperNode) {
+ DomainTreeNodeChain<int> node_path;
+ const DomainTreeNode<int>* node = NULL;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree_expose_empty_node.find(Name(names[0]),
+ &node,
+ node_path));
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+
+ const DomainTreeNode<int>* upper_node = node->getUpperNode();
+ if (upper_node_names[i] != NULL) {
+ const DomainTreeNode<int>* upper_node2 = NULL;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree_expose_empty_node.find(Name(upper_node_names[i]),
+ &upper_node2));
+ EXPECT_NE(static_cast<void*>(NULL), upper_node2);
+ EXPECT_EQ(upper_node, upper_node2);
+ } else {
+ EXPECT_EQ(static_cast<void*>(NULL), upper_node);
+ }
+
+ node = rbtree_expose_empty_node.nextNode(node_path);
+ }
+
+ // We should have reached the end of the tree.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+TEST_F(DomainTreeTest, nextNode) {
+ DomainTreeNodeChain<int> node_path;
+ const DomainTreeNode<int>* node = NULL;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[0]), &node, node_path));
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
+ node = rbtree.nextNode(node_path);
+ }
+
+ // We should have reached the end of the tree.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Just walk using previousNode until the beginning of the tree and check it is
+// OK
+//
+// rbtree - the tree to walk
+// node - result of previous call to find(), starting position of the walk
+// node_path - the path from the previous call to find(), will be modified
+// chain_length - the number of names that should be in the chain to be walked
+// (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
+// this is always from the beginning of the names[] list).
+// skip_first - if this is false, the node should already contain the node with
+// the first name of the chain. If it is true, the node should be NULL
+// (true is for finds that return no match, false for the ones that return
+// match)
+void
+previousWalk(DomainTree<int>& rbtree, const DomainTreeNode<int>* node,
+ DomainTreeNodeChain<int>& node_path, size_t chain_length,
+ bool skip_first)
+{
+ if (skip_first) {
+ // If the first is not found, this is supposed to be NULL and we skip
+ // it in our checks.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ node = rbtree.previousNode(node_path);
+ }
+ for (size_t i(chain_length); i > 0; --i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
+ // Find the node at the path and check the value is the same
+ // (that it really returns the correct corresponding node)
+ //
+ // The "empty" nodes can not be found
+ if (node->getData()) {
+ const DomainTreeNode<int>* node2(NULL);
+ DomainTreeNodeChain<int> node_path2;
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[i - 1]), &node2, node_path2));
+ EXPECT_EQ(node, node2);
+ }
+ node = rbtree.previousNode(node_path);
+ }
+
+ // We should have reached the start of the tree.
+ ASSERT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(".", node->getLabels().toText());
+
+ // With one more call it results in NULL
+ node = rbtree.previousNode(node_path);
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+
+ // Calling previousNode() yet again should still return NULL without
+ // fail.
+ node = rbtree.previousNode(node_path);
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Check the previousNode
+TEST_F(DomainTreeTest, previousNode) {
+ // First, iterate the whole tree from the end to the beginning.
+ DomainTreeNodeChain<int> node_path;
+ EXPECT_THROW(rbtree.previousNode(node_path), isc::BadValue) <<
+ "Throw before a search was done on the path";
+ const DomainTreeNode<int>* node(NULL);
+ {
+ SCOPED_TRACE("Iterate through");
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[name_count - 1]), &node, node_path));
+ previousWalk(rbtree, node, node_path, name_count, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Iterate from the middle");
+ // Now, start somewhere in the middle, but within the real node.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[4]), &node, node_path));
+ previousWalk(rbtree, node, node_path, 5, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start at the first");
+ // If we start at the lowest (which is "a"), we get to the beginning
+ // right away.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[0]), &node, node_path));
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ node = rbtree.previousNode(node_path);
+ ASSERT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(".", node->getLabels().toText());
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start before the first");
+ // If we start before the lowest (. < 0. < a.), we should not get a
+ // node. Its previous node should be the root.
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ rbtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ node = rbtree.previousNode(node_path);
+ ASSERT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(".", node->getLabels().toText());
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start after the last");
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ rbtree.find(Name("z"), &node, node_path));
+ previousWalk(rbtree, node, node_path, name_count, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start below a leaf");
+ // We exit a leaf by going down. We should start by the one
+ // we exited - 'c' (actually, we should get it by the find, as partial
+ // match).
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ rbtree.find(Name("b.c"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 3, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the right of a leaf");
+ // When searching for this, we exit the 'x' node to the right side,
+ // so we should go x afterwards.
+
+ // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+ // and not PARTIALMATCH.
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ rbtree.find(Name("xy.d.e.f"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 5, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the left of a leaf");
+ // This is similar to the previous, but we exit the 'z' leaf to the
+ // left side, so should not visit z at all then.
+
+ // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+ // and not PARTIALMATCH.
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ rbtree.find(Name("yz.d.e.f"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 9, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the right of a parent");
+ // When searching for this, we exit the 'g.h' node to the right
+ // side, so we should go to g.h's children afterwards.
+
+ // 'g.h' is an empty node, so we get a NOTFOUND and not
+ // PARTIALMATCH.
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ rbtree.find(Name("x.h"), &node, node_path));
+ // 'g.h' is the COMMONANCESTOR.
+ EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
+ EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
+ node_path.getLastComparisonResult().getRelation());
+ // find() exits to the right of 'g.h'
+ EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
+ // We then descend into 'i.g.h' and walk all the nodes in the
+ // tree.
+ previousWalk(rbtree, node, node_path, name_count, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start inside a wrong node");
+ // The d.e.f is a single node, but we want only part of it. We
+ // should start iterating before it.
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ rbtree.find(Name("e.f"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 3, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Lookup in empty tree");
+ // Just check it doesn't crash, etc.
+ TreeHolder tree_holder(mem_sgmt_, DomainTree<int>::create(mem_sgmt_));
+ DomainTree<int>& empty_tree(*tree_holder.get());
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ empty_tree.find(Name("x"), &node, node_path));
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ EXPECT_EQ(static_cast<void*>(NULL),
+ empty_tree.previousNode(node_path));
+ node = NULL;
+ node_path.clear();
+ }
+}
+
+TEST_F(DomainTreeTest, nextNodeError) {
+ // Empty chain for nextNode() is invalid.
+ DomainTreeNodeChain<int> chain;
+ EXPECT_THROW(rbtree.nextNode(chain), BadValue);
+}
+
+// A helper function for getLastComparedNode() below.
+void
+comparisonChecks(const DomainTreeNodeChain<int>& chain,
+ int expected_order, int expected_common_labels,
+ NameComparisonResult::NameRelation expected_reln)
+{
+ if (expected_order > 0) {
+ EXPECT_LT(0, chain.getLastComparisonResult().getOrder());
+ } else if (expected_order < 0) {
+ EXPECT_GT(0, chain.getLastComparisonResult().getOrder());
+ } else {
+ EXPECT_EQ(0, chain.getLastComparisonResult().getOrder());
+ }
+ EXPECT_EQ(expected_common_labels,
+ chain.getLastComparisonResult().getCommonLabels());
+ EXPECT_EQ(expected_reln,
+ chain.getLastComparisonResult().getRelation());
+}
+
+TEST_F(DomainTreeTest, getLastComparedNode) {
+ DomainTree<int>& tree = rbtree_expose_empty_node; // use the "empty OK" mode
+ DomainTreeNodeChain<int> chain;
+
+ // initially there should be no 'last compared'.
+ EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
+
+ // A search for an empty tree should result in no 'last compared', too.
+ TreeHolder tree_holder(mem_sgmt_, DomainTree<int>::create(mem_sgmt_));
+ DomainTree<int>& empty_tree(*tree_holder.get());
+ EXPECT_EQ(DomainTree<int>::NOTFOUND,
+ empty_tree.find(Name("a"), &crbtnode, chain));
+ EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
+ chain.clear();
+
+ const DomainTreeNode<int>* expected_node = NULL;
+
+ // Exact match case. The returned node should be last compared.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(Name("x.d.e.f"), &expected_node, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // 1 = # labels of "x" (note: excluding ".")
+ comparisonChecks(chain, 0, 1, NameComparisonResult::EQUAL);
+ chain.clear();
+
+ // Partial match, search stopped at the matching node, which should be
+ // the last compared node.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(Name("k.g.h"), &expected_node));
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("x.k.g.h"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // k.g.h < x.k.g.h, 1 = # labels of "k"
+ comparisonChecks(chain, 1, 1, NameComparisonResult::SUBDOMAIN);
+ chain.clear();
+
+ // Partial match, search stopped in the subtree below the matching node
+ // after following a left branch.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(Name("x.d.e.f"), &expected_node));
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("a.d.e.f"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // a < x, no common labels
+ comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
+ chain.clear();
+
+ // Partial match, search stopped in the subtree below the matching node
+ // after following a right branch.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(Name("z.d.e.f"), &expected_node));
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("zz.d.e.f"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // zz > z, no common label
+ comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
+ chain.clear();
+
+ // Partial match, search stopped at a node for a super domain of the
+ // search name in the subtree below the matching node.
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ tree.find(Name("w.y.d.e.f"), &expected_node));
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("y.d.e.f"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // y < w.y, 1 = # labels of "y"
+ comparisonChecks(chain, -1, 1, NameComparisonResult::SUPERDOMAIN);
+ chain.clear();
+
+ // Partial match, search stopped at a node that share a common ancestor
+ // with the search name in the subtree below the matching node.
+ // (the expected node is the same as the previous case)
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("z.y.d.e.f"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // z.y > w.y, 1 = # labels of "y"
+ comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
+ chain.clear();
+
+ // Search stops in the highest level (under ".") after following a left
+ // branch. (find() still returns PARTIALMATCH due to the top level ".")
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, tree.find(Name("c"), &expected_node));
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("bb"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // bb < c, no common label
+ comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
+ chain.clear();
+
+ // Search stops in the highest level (under ".") after following a right
+ // branch. (the expected node is the same as the previous case)
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ tree.find(Name("d"), &crbtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // d > c, no common label
+ comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
+ chain.clear();
+}
+
+TEST_F(DomainTreeTest, dumpTree) {
+ std::ostringstream str;
+ std::ostringstream str2;
+ rbtree.dumpTree(str);
+ str2 << "tree has 15 node(s)\n"
+ ". (black) [invisible] [subtreeroot]\n"
+ " begin down from .\n"
+ " b (black) [subtreeroot]\n"
+ " a (black)\n"
+ " NULL\n"
+ " NULL\n"
+ " d.e.f (black) [invisible]\n"
+ " begin down from d.e.f\n"
+ " w.y (black) [invisible] [subtreeroot]\n"
+ " begin down from w.y\n"
+ " p (black) [subtreeroot]\n"
+ " o (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " q (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from w.y\n"
+ " x (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " z (red)\n"
+ " begin down from z\n"
+ " j (black) [subtreeroot]\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from z\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from d.e.f\n"
+ " c (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " g.h (red)\n"
+ " begin down from g.h\n"
+ " i (black) [subtreeroot]\n"
+ " NULL\n"
+ " k (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from g.h\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from .\n"
+ " NULL\n"
+ " NULL\n";
+ EXPECT_EQ(str2.str(), str.str());
+}
+
+TEST_F(DomainTreeTest, swap) {
+ // Store info about the first tree
+ std::ostringstream str1;
+ rbtree.dumpTree(str1);
+ size_t count1(rbtree.getNodeCount());
+
+ // Create second one and store state
+ TreeHolder tree_holder(mem_sgmt_, DomainTree<int>::create(mem_sgmt_));
+ DomainTree<int>& tree2(*tree_holder.get());
+ DomainTreeNode<int>* node;
+ tree2.insert(mem_sgmt_, Name("second"), &node);
+ std::ostringstream str2;
+ tree2.dumpTree(str2);
+
+ // Swap them
+ ASSERT_NO_THROW(tree2.swap(rbtree));
+
+ // Check their sizes
+ ASSERT_EQ(1, rbtree.getNodeCount());
+ ASSERT_EQ(count1, tree2.getNodeCount());
+
+ // And content
+ std::ostringstream out;
+ rbtree.dumpTree(out);
+ ASSERT_EQ(str2.str(), out.str());
+ out.str("");
+ tree2.dumpTree(out);
+ ASSERT_EQ(str1.str(), out.str());
+}
+
+// Matching in the "root zone" may be special (e.g. there's no parent,
+// any domain names should be considered a subdomain of it), so it makes
+// sense to test cases with the root zone explicitly.
+TEST_F(DomainTreeTest, root) {
+ TreeHolder tree_holder(mem_sgmt_, DomainTree<int>::create(mem_sgmt_));
+ DomainTree<int>& root(*tree_holder.get());
+ root.insert(mem_sgmt_, Name::ROOT_NAME(), &rbtnode);
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(1)));
+
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ root.find(Name::ROOT_NAME(), &crbtnode));
+ EXPECT_EQ(rbtnode, crbtnode);
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ root.find(Name("example.com"), &crbtnode));
+ EXPECT_EQ(rbtnode, crbtnode);
+
+ // Insert a new name that better matches the query name. find() should
+ // find the better one.
+ root.insert(mem_sgmt_, Name("com"), &rbtnode);
+ rbtnode->setData(DomainTreeNode<int>::NodeDataPtr(new int(2)));
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ root.find(Name("example.com"), &crbtnode));
+ EXPECT_EQ(rbtnode, crbtnode);
+
+ // Perform the same tests for the tree that allows matching against empty
+ // nodes.
+ TreeHolder tree_holder_emptyok(mem_sgmt_,
+ DomainTree<int>::create(mem_sgmt_, true));
+ DomainTree<int>& root_emptyok(*tree_holder_emptyok.get());
+ root_emptyok.insert(mem_sgmt_, Name::ROOT_NAME(), &rbtnode);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH,
+ root_emptyok.find(Name::ROOT_NAME(), &crbtnode));
+ EXPECT_EQ(rbtnode, crbtnode);
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ root_emptyok.find(Name("example.com"), &crbtnode));
+ EXPECT_EQ(rbtnode, crbtnode);
+
+ root.insert(mem_sgmt_, Name("com"), &rbtnode);
+ EXPECT_EQ(DomainTree<int>::PARTIALMATCH,
+ root.find(Name("example.com"), &crbtnode));
+ EXPECT_EQ(rbtnode, crbtnode);
+}
+}