pike.git / src / rbtree_low.h

version» Context lines:

pike.git/src/rbtree_low.h:1: + /* The lower level api for using rbtree. This is in a separate file +  * since it's quite macro heavy. +  * +  * Created 2001-04-27 by Martin Stjernholm +  * +  * $Id: rbtree_low.h,v 1.1 2001/12/10 00:58:44 mast Exp $ +  */    -  + #ifndef RBTREE_LOW_H + #define RBTREE_LOW_H +  + #include "rbtree.h" +  + /* A sliced stack is used to track the way down in a tree, so we can +  * back up again easily while rebalancing it. The first slice is +  * allocated on the C stack. */ +  + #define STACK_SLICE_SIZE 20 + /* This is in the worst possible case enough for trees of size +  * 2^(20/2) = 1024 before allocating another slice, but in the typical +  * case it's enough for trees with up to about 2^(20-1) = 524288 +  * elements. */ +  + struct rbstack_slice + { + #ifdef RB_STATS +  size_t depth, maxdepth; + #endif +  struct rbstack_slice *up; +  struct rb_node_hdr *stack[STACK_SLICE_SIZE]; + }; +  + struct rbstack_ptr + { +  struct rbstack_slice *slice; +  size_t ssp; /* Only zero when the stack is empty. */ + }; +  + void rbstack_push (struct rbstack_ptr *rbstack, struct rb_node_hdr *node); + void rbstack_pop (struct rbstack_ptr *rbstack); + void rbstack_up (struct rbstack_ptr *rbstack); + void rbstack_up_to_root (struct rbstack_ptr *rbstack); + void rbstack_free (struct rbstack_ptr *rbstack); + void rbstack_insert (struct rbstack_ptr *top, struct rbstack_ptr *pos, +  struct rb_node_hdr *node); + void rbstack_assign (struct rbstack_ptr *target, struct rbstack_ptr *source); + void rbstack_copy (struct rbstack_ptr *target, struct rbstack_ptr *source); + void rbstack_shift (struct rbstack_ptr rbstack, +  struct rb_node_hdr *oldbase, +  struct rb_node_hdr *newbase); +  + #define RBSTACK_INIT(rbstack) \ +  struct rbstack_slice PIKE_CONCAT3 (_, rbstack, _top_) = { \ +  DO_IF_RB_STATS (0 COMMA 0 COMMA) \ +  NULL, \ +  {} \ +  }; \ +  struct rbstack_ptr rbstack = { \ +  (PIKE_CONCAT3 (_, rbstack, _top_).up = NULL, \ +  &PIKE_CONCAT3 (_, rbstack, _top_)), \ +  0 \ +  } +  + #define RBSTACK_PUSH(rbstack, node) do { \ +  if ((rbstack).ssp < STACK_SLICE_SIZE) { \ +  (rbstack).slice->stack[(rbstack).ssp++] = (node); \ +  } \ +  else rbstack_push (&(rbstack), node); \ +  DO_IF_RB_STATS ( \ +  if (++(rbstack).slice->depth > (rbstack).slice->maxdepth) \ +  (rbstack).slice->maxdepth = (rbstack).slice->depth; \ +  ); \ +  } while (0) +  + #define RBSTACK_POP(rbstack, node) do { \ +  if ((rbstack).ssp) { \ +  (node) = (rbstack).slice->stack[--(rbstack).ssp]; \ +  DO_IF_RB_STATS ((rbstack).slice->depth--); \ +  if (!(rbstack).ssp && (rbstack).slice->up) \ +  rbstack_pop (&(rbstack)); \ +  } \ +  else (node) = NULL; \ +  } while (0) +  + #define RBSTACK_POP_IGNORE(rbstack) do { \ +  if ((rbstack).ssp && !--(rbstack).ssp) { \ +  DO_IF_RB_STATS ((rbstack).slice->depth--); \ +  if ((rbstack).slice->up) \ +  rbstack_pop (&(rbstack)); \ +  } \ +  } while (0) +  + #define RBSTACK_UP(rbstack, node) do { \ +  if ((rbstack).ssp) { \ +  (node) = (rbstack).slice->stack[--(rbstack).ssp]; \ +  if (!(rbstack).ssp && (rbstack).slice->up) \ +  rbstack_up (&(rbstack)); \ +  } \ +  else (node) = NULL; \ +  } while (0) +  + #define RBSTACK_UP_IGNORE(rbstack) do { \ +  if ((rbstack).ssp && !--(rbstack).ssp && (rbstack).slice->up) \ +  rbstack_up (&(rbstack)); \ +  } while (0) +  + #define RBSTACK_PEEK(rbstack) \ +  ((rbstack).ssp ? (rbstack).slice->stack[(rbstack).ssp - 1] : NULL) +  + #define RBSTACK_POKE(rbstack, node) do { \ +  DO_IF_DEBUG (if (!(rbstack).ssp) fatal ("Using free stack pointer.\n")); \ +  (rbstack).slice->stack[(rbstack).ssp - 1] = (node); \ +  } while (0) +  + #define RBSTACK_UP_TO_ROOT(rbstack, node) do { \ +  if ((rbstack).ssp) { \ +  rbstack_up_to_root (&(rbstack)); \ +  (node) = (rbstack).slice->stack[0]; \ +  } \ +  } while (0) +  + #define RBSTACK_FREE(rbstack) do { \ +  if ((rbstack).ssp) { \ +  if ((rbstack).slice->up) rbstack_free (&(rbstack)); \ +  (rbstack).ssp = 0; \ +  } \ +  DO_IF_RB_STATS ( \ +  rb_num_tracks++; \ +  rb_track_depth += (rbstack).slice->maxdepth; \ +  if ((rbstack).slice->maxdepth > rb_max_depth) \ +  rb_max_depth = (rbstack).slice->maxdepth; \ +  (rbstack).slice->depth = (rbstack).slice->maxdepth = 0; \ +  ); \ +  } while (0) +  + #define RBSTACK_FREE_SET_ROOT(rbstack, node) do { \ +  if ((rbstack).ssp) { \ +  if ((rbstack).slice->up) rbstack_free (&(rbstack)); \ +  (node) = (rbstack).slice->stack[0]; \ +  } \ +  DO_IF_RB_STATS ( \ +  rb_num_tracks++; \ +  rb_track_depth += (rbstack).slice->maxdepth; \ +  if ((rbstack).slice->maxdepth > rb_max_depth) \ +  rb_max_depth = (rbstack).slice->maxdepth; \ +  (rbstack).slice->depth = (rbstack).slice->maxdepth = 0; \ +  ); \ +  } while (0) +  + void low_rb_init_root (struct rb_node_hdr *new_root); + void low_rb_link_at_prev (struct rb_node_hdr **root, struct rbstack_ptr rbstack, +  struct rb_node_hdr *new); + void low_rb_link_at_next (struct rb_node_hdr **root, struct rbstack_ptr rbstack, +  struct rb_node_hdr *new); + struct rb_node_hdr *low_rb_unlink_with_move (struct rb_node_hdr **root, +  struct rbstack_ptr *rbstack_ptr, +  int keep_rbstack, +  size_t node_size); + void low_rb_unlink_without_move (struct rb_node_hdr **root, +  struct rbstack_ptr *rbstack_ptr, +  int keep_rbstack); + void low_rb_build_stack (struct rb_node_hdr *root, struct rb_node_hdr *node, +  struct rbstack_ptr *rbstack); +  + #if defined (PIKE_DEBUG) || defined (TEST_MULTISET) +  + typedef void dump_data_fn (struct rb_node_hdr *node, void *extra); + void debug_dump_rb_tree (struct rb_node_hdr *root, dump_data_fn *dump_data, void *extra); + void debug_dump_rbstack (struct rbstack_ptr rbstack, struct rbstack_ptr *pos); + void debug_check_rb_tree (struct rb_node_hdr *root, dump_data_fn *dump_data, void *extra); + void debug_check_rbstack (struct rb_node_hdr *root, struct rbstack_ptr rbstack); +  + #endif +  + /* Traverses the tree in depth-first order: +  * push Run when entering the node. Preceded by an enter_* label. +  * p_leaf Run when the prev pointer of the node isn't a subtree. +  * p_sub Run when the prev pointer of the node is a subtree. +  * between Run after the prev subtree has been recursed and before +  * the next subtree is examined. Preceded by a between_* +  * label. +  * n_leaf Run when the next pointer of the node isn't a subtree. +  * n_sub Run when the next pointer of the node is a subtree. +  * pop Run when leaving the node. Preceded by a leave_* label. +  */ + #define LOW_RB_TRAVERSE(label, rbstack, node, push, p_leaf, p_sub, \ +  between, n_leaf, n_sub, pop) \ +  do { \ +  struct rb_node_hdr *rb_last_; \ +  DO_IF_RB_STATS (rb_num_traverses++); \ +  if (node) { \ +  PIKE_CONCAT (enter_, label): \ +  DO_IF_RB_STATS (rb_num_traverse_ops++); \ +  {push;} \ +  if ((node)->flags & RB_THREAD_PREV) \ +  {p_leaf;} \ +  else { \ +  {p_sub;} \ +  RBSTACK_PUSH (rbstack, node); \ +  (node) = (node)->prev; \ +  goto PIKE_CONCAT (enter_, label); \ +  } \ +  PIKE_CONCAT (between_, label): \ +  {between;} \ +  if ((node)->flags & RB_THREAD_NEXT) \ +  {n_leaf;} \ +  else { \ +  {n_sub;} \ +  RBSTACK_PUSH (rbstack, node); \ +  (node) = (node)->next; \ +  goto PIKE_CONCAT (enter_, label); \ +  } \ +  while (1) { \ +  PIKE_CONCAT (leave_, label): \ +  DO_IF_RB_STATS (rb_num_traverse_ops++); \ +  {pop;} \ +  rb_last_ = (node); \ +  RBSTACK_POP (rbstack, node); \ +  if (!(node)) break; \ +  if (rb_last_ == (node)->prev) \ +  goto PIKE_CONCAT (between_, label); \ +  } \ +  } \ +  } while (0) +  + #define LOW_RB_DEBUG_TRAVERSE(label, rbstack, node, push, p_leaf, p_sub, \ +  between, n_leaf, n_sub, pop) \ +  do { \ +  size_t PIKE_CONCAT (depth_, label) = 0; \ +  LOW_RB_TRAVERSE( \ +  label, rbstack, node, \ +  fprintf (stderr, "%*s%p enter\n", \ +  PIKE_CONCAT (depth_, label)++, "", node); {push;}, \ +  fprintf (stderr, "%*s%p prev leaf\n", \ +  PIKE_CONCAT (depth_, label), "", node); {p_leaf;}, \ +  fprintf (stderr, "%*s%p prev subtree\n", \ +  PIKE_CONCAT (depth_, label), "", node); {p_sub;}, \ +  fprintf (stderr, "%*s%p between\n", \ +  PIKE_CONCAT (depth_, label) - 1, "", node); {between;}, \ +  fprintf (stderr, "%*s%p next leaf\n", \ +  PIKE_CONCAT (depth_, label), "", node); {n_leaf;}, \ +  fprintf (stderr, "%*s%p next subtree\n", \ +  PIKE_CONCAT (depth_, label), "", node); {n_sub;}, \ +  fprintf (stderr, "%*s%p leave\n", \ +  --PIKE_CONCAT (depth_, label), "", node); {pop;}); \ +  } while (0) +  + /* The `cmp' code should set the variable cmp_res to the result of the +  * comparison between the key and the current node `node'. */ + #define LOW_RB_FIND(node, cmp, got_lt, got_eq, got_gt) \ +  do { \ +  int cmp_res, found_eq_ = 0; \ +  DO_IF_RB_STATS ( \ +  size_t stat_depth_count_ = 0; \ +  rb_num_finds++; \ +  ); \ +  while (1) { \ +  DO_IF_DEBUG (if (!node) fatal ("Recursing into null node.\n")); \ +  DO_IF_RB_STATS ( \ +  if (++stat_depth_count_ > rb_max_depth) \ +  rb_max_depth = stat_depth_count_; \ +  rb_find_depth++; \ +  ); \ +  {cmp;} \ +  if (cmp_res < 0) \ +  if ((node)->flags & RB_THREAD_PREV) \ +  if (found_eq_) \ +  (node) = (node)->prev; \ +  else { \ +  {got_gt;} \ +  break; \ +  } \ +  else { \ +  (node) = (node)->prev; \ +  continue; \ +  } \ +  else \ +  if ((node)->flags & RB_THREAD_NEXT) \ +  if (!cmp_res) \ +  {} \ +  else { \ +  {got_lt;} \ +  break; \ +  } \ +  else { \ +  if (!cmp_res) found_eq_ = 1; \ +  (node) = (node)->next; \ +  continue; \ +  } \ +  {got_eq;} \ +  break; \ +  } \ +  } while (0) +  + /* Variant of LOW_RB_FIND that assumes that `cmp' never returns 0. */ + #define LOW_RB_FIND_NEQ(node, cmp, got_lt, got_gt) \ +  do { \ +  int cmp_res; \ +  DO_IF_RB_STATS ( \ +  size_t stat_depth_count_ = 0; \ +  rb_num_finds++; \ +  ); \ +  while (1) { \ +  DO_IF_DEBUG (if (!node) fatal ("Recursing into null node.\n")); \ +  DO_IF_RB_STATS ( \ +  if (++stat_depth_count_ > rb_max_depth) \ +  rb_max_depth = stat_depth_count_; \ +  rb_find_depth++; \ +  ); \ +  {cmp;} \ +  if (cmp_res < 0) { \ +  if ((node)->flags & RB_THREAD_PREV) { \ +  {got_gt;} \ +  break; \ +  } \ +  (node) = (node)->prev; \ +  } \ +  else { \ +  DO_IF_DEBUG (if (!cmp_res) fatal ("cmp_res 0 not expected.\n")); \ +  if ((node)->flags & RB_THREAD_NEXT) { \ +  {got_lt;} \ +  break; \ +  } \ +  (node) = (node)->next; \ +  } \ +  } \ +  } while (0) +  + /* Tracks the way down a tree to a specific node and updates the stack +  * as necessary for low_rb_link_* and low_rb_unlink_*. */ + #define LOW_RB_TRACK(rbstack, node, cmp, got_lt, got_eq, got_gt) \ +  do { \ +  DO_IF_DEBUG ( \ +  if (RBSTACK_PEEK (rbstack)) fatal ("The stack is not empty.\n"); \ +  ); \ +  DO_IF_RB_STATS (rb_num_finds--); \ +  LOW_RB_FIND ( \ +  node, \ +  { \ +  DO_IF_RB_STATS (rb_find_depth--); \ +  RBSTACK_PUSH (rbstack, node); \ +  {cmp;} \ +  }, \ +  got_lt, \ +  { \ +  while ((node) != RBSTACK_PEEK (rbstack)) \ +  RBSTACK_POP_IGNORE (rbstack); \ +  {got_eq;} \ +  }, got_gt); \ +  } while (0) +  + #define LOW_RB_TRACK_NEQ(rbstack, node, cmp, got_lt, got_gt) \ +  do { \ +  DO_IF_DEBUG ( \ +  if (RBSTACK_PEEK (rbstack)) fatal ("The stack is not empty.\n"); \ +  ); \ +  DO_IF_RB_STATS (rb_num_finds--); \ +  LOW_RB_FIND_NEQ ( \ +  node, \ +  { \ +  DO_IF_RB_STATS (rb_find_depth--); \ +  RBSTACK_PUSH (rbstack, node); \ +  {cmp;} \ +  }, \ +  got_lt, got_gt); \ +  } while (0) +  + /* Goes to the first node in a tree while keeping the stack updated. */ + #define LOW_RB_TRACK_FIRST(rbstack, node) \ +  do { \ +  DO_IF_DEBUG ( \ +  if (RBSTACK_PEEK (rbstack)) fatal ("The stack is not empty.\n"); \ +  ); \ +  DO_IF_RB_STATS (rb_num_sidetracks++); \ +  if (node) { \ +  struct rb_node_hdr *rb_prev_ = node->prev; \ +  RBSTACK_PUSH (rbstack, node); \ +  DO_IF_RB_STATS (rb_num_sidetrack_ops++); \ +  while (rb_prev_) { \ +  RBSTACK_PUSH (rbstack, node = rb_prev_); \ +  DO_IF_RB_STATS (rb_num_sidetrack_ops++); \ +  rb_prev_ = node->prev; \ +  } \ +  } \ +  } while (0) +  + /* Goes to the next node in order while keeping the stack updated. */ + #define LOW_RB_TRACK_NEXT(rbstack, node) \ +  do { \ +  DO_IF_DEBUG ( \ +  if (node != RBSTACK_PEEK (rbstack)) \ +  fatal ("Given node is not on top of stack.\n"); \ +  ); \ +  DO_IF_RB_STATS (rb_num_sidetracks++); \ +  if (node->flags & RB_THREAD_NEXT) { \ +  struct rb_node_hdr *rb_next_ = node->next; \ +  while ((node = RBSTACK_PEEK (rbstack)) != rb_next_) { \ +  RBSTACK_POP_IGNORE (rbstack); \ +  DO_IF_RB_STATS (rb_num_sidetrack_ops++); \ +  } \ +  } \ +  else { \ +  node = node->next; \ +  while (1) { \ +  RBSTACK_PUSH (rbstack, node); \ +  DO_IF_RB_STATS (rb_num_sidetrack_ops++); \ +  if (node->flags & RB_THREAD_PREV) break; \ +  node = node->prev; \ +  } \ +  } \ +  } while (0) +  + /* Goes to the previous node in order while keeping the stack updated. */ + #define LOW_RB_TRACK_PREV(rbstack, node) \ +  do { \ +  DO_IF_DEBUG ( \ +  if (node != RBSTACK_PEEK (rbstack)) \ +  fatal ("Given node is not on top of stack.\n"); \ +  ); \ +  DO_IF_RB_STATS (rb_num_sidetracks++); \ +  if (node->flags & RB_THREAD_PREV) { \ +  struct rb_node_hdr *rb_prev_ = node->prev; \ +  while ((node = RBSTACK_PEEK (rbstack)) != rb_prev_) { \ +  RBSTACK_POP_IGNORE (rbstack); \ +  DO_IF_RB_STATS (rb_num_sidetrack_ops++); \ +  } \ +  } \ +  else { \ +  node = node->prev; \ +  while (1) { \ +  RBSTACK_PUSH (rbstack, node); \ +  DO_IF_RB_STATS (rb_num_sidetrack_ops++); \ +  if (node->flags & RB_THREAD_NEXT) break; \ +  node = node->next; \ +  } \ +  } \ +  } while (0) +  + /* An alternative to rb_insert, which might or might not insert the +  * newly created node. This one compares nodes like LOW_RB_FIND and +  * will only run the code `insert' when a new node actually is to be +  * inserted, otherwise it runs the code `replace' on the matching +  * existing node. */ + #define LOW_RB_INSERT(tree, node, cmp, insert, replace) \ +  do { \ +  int rb_ins_type_; \ +  RBSTACK_INIT (rbstack); \ +  if (((node) = *(tree))) { \ +  LOW_RB_TRACK ( \ +  rbstack, node, cmp, \ +  { \ +  rb_ins_type_ = 1; /* Got less - link at next. */ \ +  }, { \ +  rb_ins_type_ = 0; /* Got equal - replace. */ \ +  {replace;} \ +  RBSTACK_FREE (rbstack); \ +  }, { \ +  rb_ins_type_ = 2; /* Got greater - link at prev. */ \ +  }); \ +  } \ +  else rb_ins_type_ = 3; \ +  if (rb_ins_type_) { \ +  DO_IF_DEBUG ((node) = 0); \ +  {insert;} \ +  switch (rb_ins_type_) { \ +  case 1: low_rb_link_at_next ((tree), rbstack, (node)); break; \ +  case 2: low_rb_link_at_prev ((tree), rbstack, (node)); break; \ +  case 3: low_rb_init_root (*(tree) = (node)); break; \ +  } \ +  } \ +  } while (0) +  + /* Merges the two trees a and b in linear time (no more, no less). The +  * operation argument describes the way to merge, like the one given +  * to merge() in array.c. cmp does the comparison between a and b to +  * cmp_res, copy_a and copy_b copy the nodes a and b resp, to +  * new_node. free_a and free_b free the nodes a and b resp. prep_a and +  * prep_b is run for every visited node in a and b resp, before any of +  * the other code blocks. +  * +  * The result in res is a list linked by the next pointers, and length +  * is set to the length of it. These are suitable for rb_make_tree. +  * +  * NB: It doesn't handle making duplicates of the same node, i.e. +  * PIKE_ARRAY_OP_A without PIKE_ARRAY_OP_SKIP_A. Not a problem since +  * none of the currently defined operations use that. */ + #define LOW_RB_MERGE(label, a, b, res, length, operation, \ +  prep_a, prep_b, cmp, copy_a, free_a, copy_b, free_b) \ +  do { \ +  struct rb_node_hdr *new_node; \ +  int cmp_res, op_ = 0; /* Init only to avoid warnings. */ \ +  /* Traverse backwards so that the merge "gravitates" towards the */ \ +  /* end when duplicate entries are processed, e.g. */ \ +  /* (<1:1, 1:2>) | (<1:3>) produces (<1:1, 1:3>) and not */ \ +  /* (<1:3, 1:2>). */ \ +  \ +  a = rb_last (a); \ +  b = rb_last (b); \ +  res = 0; \ +  \ +  while (1) { \ +  /* A bit quirky code to avoid expanding the code blocks more */ \ +  /* than once. */ \ +  if (a) {prep_a;} \ +  if (b) { \ +  {prep_b;} \ +  if (a) { \ +  {cmp;} \ +  /* Result reversed due to backward direction. */ \ +  if (cmp_res > 0) \ +  op_ = operation >> 8; \ +  else if (cmp_res < 0) \ +  op_ = operation; \ +  else \ +  op_ = operation >> 4; \ +  } \ +  else if (operation & PIKE_ARRAY_OP_B) \ +  goto PIKE_CONCAT (label, _copy_b); \ +  else \ +  goto PIKE_CONCAT (label, _free_b); \ +  } \ +  else \ +  if (a) \ +  if (operation & (PIKE_ARRAY_OP_A << 8)) \ +  goto PIKE_CONCAT (label, _copy_a); \ +  else \ +  goto PIKE_CONCAT (label, _free_a); \ +  else \ +  break; \ +  \ +  if (op_ & PIKE_ARRAY_OP_B) { \ +  PIKE_CONCAT (label, _copy_b):; \ +  {copy_b;} \ +  new_node->next = res, res = new_node; \ +  length++; \ +  b = rb_prev (b); \ +  } \ +  else if (op_ & PIKE_ARRAY_OP_SKIP_B) { \ +  PIKE_CONCAT (label, _free_b):; \ +  new_node = rb_prev (b); \ +  {free_b;} \ +  b = new_node; \ +  } \ +  \ +  if (a) { \ +  if (op_ & PIKE_ARRAY_OP_A) { \ +  if (!(op_ & PIKE_ARRAY_OP_B)) { \ +  PIKE_CONCAT (label, _copy_a):; \ +  {copy_a;} \ +  new_node->next = res, res = new_node; \ +  length++; \ +  a = rb_prev (a); \ +  } \ +  } \ +  else if (op_ & PIKE_ARRAY_OP_SKIP_A) { \ +  PIKE_CONCAT (label, _free_a):; \ +  new_node = rb_prev (a); \ +  {free_a;} \ +  a = new_node; \ +  } \ +  } \ +  } \ +  } while (0) +  + #endif /* RBTREE_H */   Newline at end of file added.