rbtree.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef _RBTREE_H
  15. #define _RBTREE_H 1
  16. #include <dns/name.h>
  17. #include <boost/utility.hpp>
  18. #include <boost/pool/object_pool.hpp>
  19. #include <exception>
  20. #include <ostream>
  21. #include <algorithm>
  22. namespace isc {
  23. namespace datasrc {
  24. namespace helper {
  25. /// helper function to remove the base domain from super domain
  26. /// the precondition of this function is the super_name contains the
  27. /// sub_name
  28. isc::dns::Name
  29. operator-(const isc::dns::Name& super_name, const isc::dns::Name& sub_name) {
  30. return (super_name.split(0, super_name.getLabelCount() -
  31. sub_name.getLabelCount()));
  32. }
  33. /// for indent purpose, add certian mount empty charachter to output stream
  34. /// according to the depth,
  35. void
  36. indent(std::ostream& os, unsigned int depth) {
  37. static const unsigned int INDENT_FOR_EACH_DEPTH = 5;
  38. os << std::string(depth * INDENT_FOR_EACH_DEPTH, ' ');
  39. }
  40. }
  41. /// \brief Define rbtree color
  42. enum RBTreeColor {BLACK, RED};
  43. template <typename T>
  44. class RBTree;
  45. /// \brief \c RBNode class represents one domain name in the domain space
  46. /// It has two roles, the first one is as one node in the \c RBTree,
  47. /// the second one is to store the data related to DNS. As for the first role,
  48. /// it has left, right, parent and color members
  49. /// which is used to keep the balance of the \c RBTree. As for the second role,
  50. // it stores the RRsets that belong to the domain name and a rbtree which
  51. /// includes all the subdomains of this node.
  52. /// The name stored in the node is relative related to its parent node.
  53. /// One special kind of node is non-terminal node
  54. /// which has subdomains with RRset but itself doesn't have any RRsets. and typically
  55. /// this kind of node is shadow to end user
  56. ///
  57. /// \note: \c RBNode should be created or destroyed only by \c RBTree so
  58. /// constructor and destructor function aren't exposed. The memory management of each
  59. /// node will be handled by a pool, so the node deconstruction will do nothing
  60. template <typename T>
  61. class RBNode : public boost::noncopyable {
  62. public:
  63. /// only \c RBTree can create and destroy \c RBNode
  64. friend class RBTree<T>;
  65. /// \name Constructors and destructor
  66. //@{
  67. /// \brief Default constructor.
  68. ///
  69. /// This constructor is provided specifically for generating a special
  70. /// "null" node, and is intended be used only internally.
  71. RBNode();
  72. /// \brief Constructor from the node name.
  73. ///
  74. /// \param name The domain name corresponding to the node.
  75. RBNode(const isc::dns::Name& name);
  76. /// \brief Deconstructor.
  77. ~RBNode();
  78. //@}
  79. /// \name Test functions
  80. //@{
  81. /// \brief return the name of current node, it's relative to its parents
  82. //
  83. /// \todo Is it meaningful to return the absolute of the node?
  84. const isc::dns::Name& getName() const { return (name_); }
  85. /// \brief return the data store in this node
  86. T* getData() { return (data_); }
  87. /// \brief return the data stored in this node, read-only version
  88. const T *getData() const { return (data_);}
  89. /// \brief return whether the node has related data
  90. bool isEmpty() const { return (data_ == NULL);}
  91. //@}
  92. /// \name Modify functions
  93. /// \brief set the data stored in the node
  94. /// this function also means ownership transfer, then data is owned by
  95. /// \c RBNode, and RBNode will in charge of the memory management
  96. /// \note when set new data, old data will be deleted first
  97. void setData(T* data);
  98. private:
  99. /// This is a factory class method of a special singleton null node.
  100. static RBNode<T>* NULL_NODE() {
  101. static RBNode<T> null_node;
  102. return (&null_node);
  103. }
  104. /// \brief copy the the data saved in the node into another node
  105. /// the data copied exclude the rbtree related data like left,right,parent
  106. /// and color
  107. void swap(RBNode<T>& node);
  108. /// data to maintain the rbtree balance
  109. RBNode<T>* parent_;
  110. RBNode<T>* left_;
  111. RBNode<T>* right_;
  112. RBTreeColor color_;
  113. isc::dns::Name name_;
  114. T* data_;
  115. /// the down pointer points to the root node of sub domains of current
  116. /// domain
  117. /// \par Adding down pointer to \c RBNode is for two purpose:
  118. /// \li Accelerate the search process, with sub domain tree, it split the
  119. /// big flat tree into several hierarchy trees
  120. /// \li It save memory useage, so same label won't be saved several times
  121. RBNode<T>* down_;
  122. };
  123. // typically each node should has a name associate with it
  124. // this construction is only used to create \c NULLNODE
  125. template <typename T>
  126. RBNode<T>::RBNode() :
  127. parent_(this),
  128. left_(this),
  129. right_(this),
  130. color_(BLACK),
  131. // dummy name, the value doesn't matter:
  132. name_(isc::dns::Name::ROOT_NAME()),
  133. data_(NULL),
  134. down_(NULL)
  135. {
  136. }
  137. template <typename T>
  138. RBNode<T>::RBNode(const isc::dns::Name& name) :
  139. parent_(NULL_NODE()),
  140. left_(NULL_NODE()),
  141. right_(NULL_NODE()),
  142. color_(RED),
  143. name_(name),
  144. data_(NULL),
  145. down_(NULL)
  146. {
  147. }
  148. template <typename T>
  149. RBNode<T>::~RBNode() {
  150. delete data_;
  151. }
  152. template <typename T>
  153. void
  154. RBNode<T>::setData(T* data) {
  155. if (data_ != data) {
  156. delete data_;
  157. data_ = data;
  158. }
  159. }
  160. template <typename T>
  161. void
  162. RBNode<T>::swap(RBNode<T>& node) {
  163. std::swap(node.name_, name_);
  164. std::swap(node.data_, data_);
  165. std::swap(node.down_, down_);
  166. }
  167. /// \brief \c RBTree class represents all the domains with the same suffix,
  168. /// so it can be used to store the domains in one zone.
  169. ///
  170. /// \c RBTree is a generic red black tree, and contains all the nodes with
  171. /// the same suffix, since each
  172. /// name may have sub domain names so \c RBTree is a recursive data structure
  173. /// or tree in tree.
  174. /// So for one zone, several RBTrees may be involved. But from outside, the sub
  175. /// tree is opaque for end users.
  176. template <typename T>
  177. class RBTree : public boost::noncopyable {
  178. friend class RBNode<T>;
  179. public:
  180. /// \brief The return value for the \c find() insert() and erase() method
  181. enum Result {
  182. SUCCEED, //insert or erase succeed
  183. EXACTMATCH, //find the target name
  184. PARTIALMATCH, //find part of target name
  185. NOTFOUND, // for find function means no related name found
  186. // for erase function means erase not exist name
  187. ALREADYEXIST, //for insert operation, the name to insert already exist
  188. NOMEM //no memory to create new node
  189. };
  190. /// \name Constructor and Destructor
  191. //@{
  192. RBTree();
  193. /// \b Note: RBTree is not intended to be inherited so the destructor
  194. /// is not virtual
  195. ~RBTree();
  196. //@}
  197. /// \name Inquery methods
  198. //@{
  199. /// \brief Find the node with the name
  200. /// \param name Target to be found
  201. /// \param node Point to the node when the return vaule is \c not
  202. /// NOTFOUND, if the return value is NOTFOUND, the value of node is
  203. /// \c unknown
  204. Result find(const isc::dns::Name& name, RBNode<T>** node) const;
  205. Result find(const isc::dns::Name& name, const RBNode<T>** node) const;
  206. /// \brief Get the total node count in the tree
  207. /// the node count including the node created common suffix node,
  208. /// this function will only be used for debuging
  209. int getNodeCount() const { return (node_count_);}
  210. /// \brief Get the total names has been inserted into the tree
  211. int getNameCount() const { return (name_count_);}
  212. //@}
  213. /// \name Debug function
  214. //@{
  215. /// \brief print the nodes in the trees
  216. /// \todo is it better to return one string instead of print to the stdout?
  217. void dumpTree(std::ostream& os, unsigned int depth = 0) const;
  218. //@}
  219. /// \name Modify function
  220. //@{
  221. /// \brief Insert the domain name into the tree
  222. /// \param name The name to be inserted into the tree
  223. /// \param inserted_node If no node with the name in the tree,
  224. /// new \c RBNode will be created, otherwise nothing will be done.
  225. /// Anyway the pointer point to the node with the name will be assigned to
  226. /// inserted_node
  227. /// \return return SUCCEED means no node exists in the tree with the name before
  228. /// insert; return ALREADYEXIST means already has the node with the given name
  229. /// return NOMEM means no memory left to allocate new node
  230. //
  231. /// To add an RRset into one node when it's not known whether the node
  232. /// already exists, it is better to call \c insert and then call the
  233. /// RBNode interface instead of calling \c find().
  234. Result insert(const isc::dns::Name& name, RBNode<T>** inserted_node);
  235. //@}
  236. private:
  237. /// \name RBTree balance functions
  238. //@{
  239. void insertRebalance(RBNode<T>** root, RBNode<T>* node);
  240. RBNode<T>* rightRotate(RBNode<T>** root, RBNode<T>* node);
  241. RBNode<T>* leftRotate(RBNode<T>** root, RBNode<T>* node);
  242. //@}
  243. /// \name Helper functions
  244. //@{
  245. /// Each public function has related recursive helper function
  246. Result findHelper(const isc::dns::Name& name, RBNode<T>** up,
  247. RBNode<T>** node) const;
  248. void dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
  249. unsigned int depth) const;
  250. /// get one node from the node pool, if no memory left return NULL
  251. /// without throw exception
  252. RBNode<T>* createNode();
  253. RBNode<T>* createNode(const isc::dns::Name& name);
  254. /// Split one node into two nodes, keep the old node and create one new
  255. /// node, old node will hold the base name, new node will be the down node
  256. /// of old node, new node will hold the sub_name, the data
  257. /// of old node will be move into new node, and old node became none-terminal
  258. /// \return NOMEM: means no memory to create new node
  259. /// otherwise return SUCCEED
  260. Result nodeFission(RBNode<T>& node, const isc::dns::Name& sub_name);
  261. //@}
  262. RBNode<T>* root_;
  263. RBNode<T>* NULLNODE;
  264. /// the node count of current tree
  265. unsigned int node_count_;
  266. /// the count of real name user inserted into the domain tree
  267. unsigned int name_count_;
  268. /// use mem pool to manage rbnode to accelerate node creation and destruction
  269. boost::object_pool<RBNode<T> > node_pool_;
  270. };
  271. /// \par
  272. /// with the following names:
  273. /// a x.d.e.f o.w.y.d.e.f
  274. /// b z.d.e.f p.w.y.d.e.f
  275. /// c g.h q.w.y.d.e.f
  276. /// the tree will looks like:
  277. /// b
  278. /// / \
  279. /// a d.e.f
  280. /// /|\
  281. /// c | g.h
  282. /// |
  283. /// w.y
  284. /// /|\
  285. /// x | z
  286. /// |
  287. /// p
  288. /// / \
  289. /// o q
  290. template <typename T>
  291. RBTree<T>::RBTree() {
  292. NULLNODE = RBNode<T>::NULL_NODE();
  293. root_ = NULLNODE;
  294. node_count_ = 0;
  295. name_count_ = 0;
  296. }
  297. template <typename T>
  298. RBTree<T>::~RBTree() {
  299. assert(root_ != NULL);
  300. }
  301. template <typename T>
  302. RBNode<T>*
  303. RBTree<T>::createNode() {
  304. return node_pool_.construct();
  305. }
  306. template <typename T>
  307. RBNode<T>*
  308. RBTree<T>::createNode(const isc::dns::Name& name) {
  309. return node_pool_.construct(name);
  310. }
  311. template <typename T>
  312. typename RBTree<T>::Result
  313. RBTree<T>::find(const isc::dns::Name& name, RBNode<T>** node) const {
  314. RBNode<T>* up_node = NULL;
  315. return (findHelper(name, &up_node, node));
  316. }
  317. template <typename T>
  318. typename RBTree<T>::Result
  319. RBTree<T>::find(const isc::dns::Name& name, const RBNode<T>** node) const {
  320. RBNode<T>* up_node;
  321. RBNode<T>* target_node;
  322. const typename RBTree<T>::Result ret =
  323. findHelper(name, &up_node, &target_node);
  324. if (ret != NOTFOUND) {
  325. *node = target_node;
  326. }
  327. return (ret);
  328. }
  329. template <typename T>
  330. typename RBTree<T>::Result
  331. RBTree<T>::findHelper(const isc::dns::Name& target_name, RBNode<T>** up_node,
  332. RBNode<T>** target) const
  333. {
  334. using namespace helper;
  335. RBNode<T>* node = root_;
  336. typename RBTree<T>::Result ret = NOTFOUND;
  337. *up_node = NULL;
  338. isc::dns::Name name = target_name;
  339. while (node != NULLNODE) {
  340. const isc::dns::NameComparisonResult compare_result =
  341. name.compare(node->name_);
  342. const isc::dns::NameComparisonResult::NameRelation relation =
  343. compare_result.getRelation();
  344. if (relation == isc::dns::NameComparisonResult::EQUAL) {
  345. if (!node->isEmpty()) {
  346. *target = node;
  347. ret = EXACTMATCH;
  348. }
  349. break;
  350. } else {
  351. const int common_label_count = compare_result.getCommonLabels();
  352. // If the common label count is 1, there is no common label between
  353. // the two names, except the trailing "dot".
  354. if (common_label_count == 1) {
  355. node = (compare_result.getOrder() < 0) ?
  356. node->left_ : node->right_;
  357. } else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
  358. *up_node = node;
  359. name = name - node->name_;
  360. if (node->isEmpty()) {
  361. assert(node->down_ != NULL);
  362. node = node->down_;
  363. } else {
  364. ret = RBTree<T>::PARTIALMATCH;
  365. *target = node;
  366. if (node->down_ != NULL)
  367. node = node->down_;
  368. else
  369. break;
  370. }
  371. } else {
  372. break;
  373. }
  374. }
  375. }
  376. return (ret);
  377. }
  378. template <typename T>
  379. typename RBTree<T>::Result
  380. RBTree<T>::insert(const isc::dns::Name& target_name, RBNode<T>** new_node) {
  381. using namespace helper;
  382. RBNode<T>* parent = NULLNODE;
  383. RBNode<T>* current = root_;
  384. RBNode<T>* up_node = NULL;
  385. isc::dns::Name name = target_name;
  386. int order = -1;
  387. while (current != NULLNODE) {
  388. const isc::dns::NameComparisonResult compare_result =
  389. name.compare(current->name_);
  390. const isc::dns::NameComparisonResult::NameRelation relation =
  391. compare_result.getRelation();
  392. if (relation == isc::dns::NameComparisonResult::EQUAL) {
  393. if (new_node != NULL) {
  394. *new_node = current;
  395. }
  396. // if the node is a common suffix not user inserted, return succeed
  397. // otherwise return already exist
  398. if (current->isEmpty()) {
  399. ++name_count_;
  400. return (SUCCEED);
  401. } else {
  402. return (ALREADYEXIST);
  403. }
  404. } else {
  405. const int common_label_count = compare_result.getCommonLabels();
  406. if (common_label_count == 1) {
  407. parent = current;
  408. order = compare_result.getOrder();
  409. current = order < 0 ? current->left_ : current->right_;
  410. } else {
  411. // insert sub domain to sub tree
  412. if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
  413. if (current->down_ == NULL) {
  414. RBNode<T>* node = createNode(name - current->name_);
  415. if (node == NULL) {
  416. return (NOMEM);
  417. }
  418. //root node of sub tree, the initial color is BLACK
  419. node->color_ = BLACK;
  420. current->down_ = node;
  421. if (new_node != NULL) {
  422. *new_node = node;
  423. }
  424. ++node_count_;
  425. ++name_count_;
  426. return (SUCCEED);
  427. } else {
  428. up_node = current;
  429. name = name - current->name_;
  430. current = current->down_;
  431. }
  432. } else {
  433. // The number of labels in common is fewer
  434. // than the number of labels at the current
  435. // node, so the current node must be adjusted
  436. // to have just the common suffix, and a down
  437. // pointer made to a new tree.
  438. const isc::dns::Name common_ancestor = name.split(
  439. name.getLabelCount() - common_label_count,
  440. common_label_count);
  441. if (nodeFission(*current, common_ancestor) == NOMEM) {
  442. return (NOMEM);
  443. }
  444. }
  445. }
  446. }
  447. }
  448. RBNode<T>** current_root = up_node ? &(up_node->down_) : &root_;
  449. RBNode<T>* node = createNode(name);
  450. if (node == NULL) {
  451. return (NOMEM);
  452. }
  453. node->parent_ = parent;
  454. if (parent == NULLNODE) {
  455. *current_root = node;
  456. } else if (order < 0) {
  457. parent->left_ = node;
  458. } else {
  459. parent->right_ = node;
  460. }
  461. insertRebalance(current_root, node);
  462. if (new_node != NULL) {
  463. *new_node = node;
  464. }
  465. ++node_count_;
  466. ++name_count_;
  467. return (SUCCEED);
  468. }
  469. template <typename T>
  470. typename RBTree<T>::Result
  471. RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
  472. using namespace helper;
  473. const isc::dns::Name sub_name = node.name_ - base_name;
  474. RBNode<T>* down_node = createNode(node.name_ - base_name);
  475. if (down_node == NULL) {
  476. return (NOMEM);
  477. }
  478. node.swap(*down_node);
  479. node.name_ = base_name;
  480. node.down_ = down_node;
  481. down_node->name_ = sub_name;
  482. //root node of sub tree, the initial color is BLACK
  483. down_node->color_ = BLACK;
  484. ++node_count_;
  485. return (SUCCEED);
  486. }
  487. template <typename T>
  488. void
  489. RBTree<T>::insertRebalance(RBNode<T>** root, RBNode<T>* node) {
  490. RBNode<T>* uncle;
  491. while (node != *root && node->parent_->color_ == RED) {
  492. if (node->parent_ == node->parent_->parent_->left_) {
  493. uncle = node->parent_->parent_->right_;
  494. if (uncle->color_ == RED) {
  495. node->parent_->color_ = BLACK;
  496. uncle->color_ = BLACK;
  497. node->parent_->parent_->color_ = RED;
  498. node = node->parent_->parent_;
  499. } else {
  500. if (node == node->parent_->right_) {
  501. node = node->parent_;
  502. leftRotate(root, node);
  503. }
  504. node->parent_->color_ = BLACK;
  505. node->parent_->parent_->color_ = RED;
  506. rightRotate(root, node->parent_->parent_);
  507. }
  508. } else {
  509. uncle = node->parent_->parent_->left_;
  510. if (uncle->color_ == RED) {
  511. node->parent_->color_ = BLACK;
  512. uncle->color_ = BLACK;
  513. node->parent_->parent_->color_ = RED;
  514. node = node->parent_->parent_;
  515. } else {
  516. if (node == node->parent_->left_) {
  517. node = node->parent_;
  518. rightRotate(root, node);
  519. }
  520. node->parent_->color_ = BLACK;
  521. node->parent_->parent_->color_ = RED;
  522. leftRotate(root, node->parent_->parent_);
  523. }
  524. }
  525. }
  526. (*root)->color_ = BLACK;
  527. }
  528. template <typename T>
  529. RBNode<T>*
  530. RBTree<T>::leftRotate(RBNode<T>** root, RBNode<T>* node) {
  531. RBNode<T>* right = node->right_;
  532. node->right_ = right->left_;
  533. if (right->left_ != NULLNODE)
  534. right->left_->parent_ = node;
  535. right->parent_ = node->parent_;
  536. if (node->parent_ != NULLNODE) {
  537. if (node == node->parent_->left_) {
  538. node->parent_->left_ = right;
  539. } else {
  540. node->parent_->right_ = right;
  541. }
  542. } else {
  543. *root = right;
  544. }
  545. right->left_ = node;
  546. node->parent_ = right;
  547. return (node);
  548. }
  549. template <typename T>
  550. RBNode<T>*
  551. RBTree<T>::rightRotate(RBNode<T>** root, RBNode<T>* node) {
  552. RBNode<T>* left = node->left_;
  553. node->left_ = left->right_;
  554. if (left->right_ != NULLNODE)
  555. left->right_->parent_ = node;
  556. left->parent_ = node->parent_;
  557. if (node->parent_ != NULLNODE) {
  558. if (node == node->parent_->right_) {
  559. node->parent_->right_ = left;
  560. } else {
  561. node->parent_->left_ = left;
  562. }
  563. } else {
  564. *root = left;
  565. }
  566. left->right_ = node;
  567. node->parent_ = left;
  568. return (node);
  569. }
  570. template <typename T>
  571. void
  572. RBTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
  573. helper::indent(os, depth);
  574. os << "tree has node(s) " << node_count_ << "\n";
  575. dumpTreeHelper(os, root_, depth);
  576. }
  577. template <typename T>
  578. void
  579. RBTree<T>::dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
  580. unsigned int depth) const
  581. {
  582. helper::indent(os, depth);
  583. os << node->name_.toText() << " ("
  584. << ((node->color_ == BLACK) ? "black" : "red") << ")";
  585. os << ((node->isEmpty()) ? "[invisible] \n" : "\n");
  586. if (node->down_ != NULL) {
  587. helper::indent(os, depth + 1);
  588. os << "begin down from "<< node->name_.toText() << "\n";
  589. dumpTreeHelper(os, node->down_, depth + 1);
  590. helper::indent(os, depth + 1);
  591. os << "end down from " << node->name_.toText() << "\n";
  592. }
  593. if (node->left_ != NULLNODE) {
  594. dumpTreeHelper(os, node->left_, depth + 1);
  595. } else {
  596. helper::indent(os, depth + 1);
  597. os << "NULL\n";
  598. }
  599. if (node->right_ != NULLNODE) {
  600. dumpTreeHelper(os, node->right_, depth + 1);
  601. } else {
  602. helper::indent(os, depth + 1);
  603. os << "NULL\n";
  604. }
  605. }
  606. }
  607. }
  608. #endif // _RBTREE_H
  609. // Local Variables:
  610. // mode: c++
  611. // End: