github.com/moontrade/nogc@v0.1.7/collections/btree/btree.c (about)

     1  // Copyright 2020 Joshua J Baker. All rights reserved.
     2  // Use of this source code is governed by an MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  #include <stdlib.h>
     6  #include <stdio.h>
     7  #include <string.h>
     8  #include <stdint.h>
     9  #include "btree.h"
    10  
    11  static void *(*_malloc)(size_t) = NULL;
    12  static void (*_free)(void *) = NULL;
    13  
    14  // btree_set_allocator allows for configuring a custom allocator for
    15  // all btree library operations. This function, if needed, should be called
    16  // only once at startup and a prior to calling btree_new().
    17  void btree_set_allocator(void *(malloc)(size_t), void (*free)(void*)) {
    18      _malloc = malloc;
    19      _free = free;
    20  }
    21  
    22  #define panic(_msg_) { \
    23      fprintf(stderr, "panic: %s (%s:%d)\n", (_msg_), __FILE__, __LINE__); \
    24      exit(1); \
    25  }(void)(0)
    26  
    27  struct node {
    28      short num_items;
    29      bool leaf;
    30      char unused[sizeof(void*)-3]; // explicit padding
    31      char *items;
    32      struct node *children[];
    33  };
    34  
    35  static void *get_item_at(size_t elsz, struct node *node, size_t index) {
    36      return node->items+elsz*index;
    37  }
    38  
    39  static void set_item_at(size_t elsz, struct node *node, size_t index,
    40                          const void *item)
    41  {
    42      memcpy(get_item_at(elsz, node, index), item, elsz);
    43  }
    44  
    45  static void copy_item_into(size_t elsz, struct node *node, size_t index,
    46                             void *into)
    47  {
    48      memcpy(into, get_item_at(elsz, node, index), elsz);
    49  }
    50  
    51  static void copy_item(size_t elsz, struct node *node_a, size_t index_a,
    52                                     struct node *node_b, size_t index_b)
    53  {
    54      memcpy(get_item_at(elsz, node_a, index_a),
    55             get_item_at(elsz, node_b, index_b), elsz);
    56  }
    57  
    58  static void swap_item_at(size_t elsz, struct node *node, size_t index,
    59                           const void *item, void *into)
    60  {
    61      void *ptr = get_item_at(elsz, node, index);
    62      memcpy(into, ptr, elsz);
    63      memcpy(ptr, item, elsz);
    64  }
    65  
    66  struct group {
    67      struct node **nodes;
    68      size_t len, cap;
    69  };
    70  
    71  struct pool {
    72      struct group leaves;
    73      struct group branches;
    74  };
    75  
    76  // btree is a standard B-tree with post-set splits.
    77  struct btree {
    78      void *(*malloc)(size_t);
    79      void *(*realloc)(void *, size_t);
    80      void (*free)(void *);
    81      int (*_compare)(const void *a, const void *b, void *udata);
    82      void *udata;
    83      struct node *root;
    84      size_t count;
    85      struct pool pool;
    86      bool oom;
    87      char unused[sizeof(void*)-1]; // explicit padding
    88      size_t height;
    89      size_t max_items;
    90      size_t min_items;
    91      size_t elsize;
    92      void *spares[3];    // holds the result of sets and deletes, etc.
    93      void *litem;        // last load item
    94      struct node *lnode; // last load node
    95  };
    96  
    97  static int btcompare(struct btree *btree, const void *a, const void *b) {
    98      return btree->_compare(a, b, btree->udata);
    99  }
   100  
   101  static struct node *node_new(struct btree *btree, bool leaf) {
   102      size_t sz = sizeof(struct node);
   103      if (!leaf) {
   104          sz += sizeof(struct node*)*btree->max_items;
   105      }
   106      size_t itemsoff = sz;
   107      sz += btree->elsize*(btree->max_items-1);
   108      struct node *node = btree->malloc(sz);
   109      if (!node) {
   110          return NULL;
   111      }
   112      node->leaf = leaf;
   113      node->num_items = 0;
   114      node->items = (char*)node+itemsoff;
   115      return node;
   116  }
   117  
   118  static void node_free(struct btree *btree, struct node *node) {
   119      if (!node->leaf) {
   120          for (int i = 0; i < node->num_items; i++) {
   121              node_free(btree, node->children[i]);
   122          }
   123          node_free(btree, node->children[node->num_items]);
   124      }
   125      btree->free(node);
   126  }
   127  
   128  static struct node *gimme_node(struct group *group) {
   129      if (group->len == 0) panic("out of nodes");
   130      return group->nodes[--group->len];
   131  }
   132  
   133  static struct node *gimme_leaf(struct btree *btree) {
   134      return gimme_node(&btree->pool.leaves);
   135  }
   136  
   137  static struct node *gimme_branch(struct btree *btree) {
   138      return gimme_node(&btree->pool.branches);
   139  }
   140  
   141  static bool grow_group(struct btree *btree, struct group *group) {
   142      size_t cap = group->cap?group->cap*2:1;
   143      struct node **nodes = btree->malloc(sizeof(struct node*)*cap);
   144      if (!nodes) {
   145          return false;
   146      }
   147      memcpy(nodes, group->nodes, group->len*sizeof(struct node*));
   148      btree->free(group->nodes);
   149      group->nodes = nodes;
   150      group->cap = cap;
   151      return true;
   152  }
   153  
   154  static void takeaway(struct btree *btree, struct node *node) {
   155      const size_t MAXLEN = 32;
   156      struct group *group;
   157      if (node->leaf) {
   158          group = &btree->pool.leaves;
   159      } else {
   160          group = &btree->pool.branches;
   161      }
   162      if (group->len == MAXLEN) {
   163          btree->free(node);
   164          return;
   165      }
   166      if (group->len == group->cap) {
   167          if (!grow_group(btree, group)) {
   168              btree->free(node);
   169              return;
   170          }
   171      }
   172      group->nodes[group->len++] = node;
   173  }
   174  
   175  // fill_pool fills the node pool prior to inserting items. This ensures there
   176  // is enough memory before we begin doing to things like splits and tree
   177  // rebalancing. There needs to be at least one available leaf and N branches
   178  // where N is equal to the height of the tree.
   179  static bool fill_pool(struct btree *btree) {
   180      if (btree->pool.leaves.len == 0) {
   181          if (btree->pool.leaves.cap == 0) {
   182              if (!grow_group(btree, &btree->pool.leaves)) {
   183                  return false;
   184              }
   185          }
   186          struct node *leaf = node_new(btree, true);
   187          if (!leaf) {
   188              return false;
   189          }
   190          btree->pool.leaves.nodes[btree->pool.leaves.len++] = leaf;
   191      }
   192      while (btree->pool.branches.len < btree->height) {
   193          if (btree->pool.branches.len == btree->pool.branches.cap) {
   194              if (!grow_group(btree, &btree->pool.branches)) {
   195                  return false;
   196              }
   197          }
   198          struct node *branch = node_new(btree, false);
   199          if (!branch) {
   200              return false;
   201          }
   202          btree->pool.branches.nodes[btree->pool.branches.len++] = branch;
   203      }
   204      return true;
   205  }
   206  
   207  static void node_join(size_t elsize, struct node *left, struct node *right) {
   208      memcpy(left->items+elsize*(size_t)left->num_items,
   209             right->items,
   210             (size_t)right->num_items*elsize);
   211      if (!left->leaf) {
   212          memcpy(&left->children[left->num_items],
   213                 &right->children[0],
   214                 (size_t)(right->num_items+1)*sizeof(struct node*));
   215      }
   216      left->num_items += right->num_items;
   217  }
   218  
   219  static void node_shift_right(size_t elsize, struct node *node, size_t index) {
   220      memmove(node->items+elsize*(index+1),
   221              node->items+elsize*index,
   222              ((size_t)node->num_items-index)*elsize);
   223      if (!node->leaf) {
   224          memmove(&node->children[index+1],
   225                  &node->children[index],
   226                  ((size_t)node->num_items-index+1)*sizeof(struct node*));
   227      }
   228      node->num_items++;
   229  }
   230  
   231  static void node_shift_left(size_t elsize, struct node *node, size_t index,
   232                              bool for_merge)
   233  {
   234      memmove(node->items+elsize*index,
   235              node->items+elsize*(index+1),
   236              ((size_t)node->num_items-index)*elsize);
   237      if (!node->leaf) {
   238          if (for_merge) {
   239              index++;
   240          }
   241          memmove(&node->children[index],
   242                  &node->children[index+1],
   243                  ((size_t)node->num_items-index+1)*sizeof(struct node*));
   244      }
   245      node->num_items--;
   246  }
   247  
   248  // btree_new returns a new B-tree.
   249  // Param `elsize` is the size of each element in the tree. Every element that
   250  // is inserted, deleted, or searched will be this size.
   251  // Param `max_items` is the maximum number of items per node. Setting this to
   252  // zero will default to 256. The max is 4096.
   253  // Param `compare` is a function that compares items in the tree. See the
   254  // qsort stdlib function for an example of how this function works.
   255  // The btree must be freed with btree_free().
   256  struct btree *btree_new(size_t elsize, size_t max_items,
   257                          int (*compare)(const void *a, const void *b,
   258                                         void *udata),
   259                          void *udata)
   260  {
   261      return btree_new_with_allocator(
   262          (_malloc?_malloc:malloc),
   263          NULL, // this library does not currently use realloc
   264          (_free?_free:free),
   265          elsize, max_items, compare, udata
   266      );
   267  }
   268  
   269  // btree_new_with_allocator returns a new btree using a custom allocator.
   270  // See btree_new for more information
   271  struct btree *btree_new_with_allocator(
   272                          void *(*malloc)(size_t),
   273                          void *(*realloc)(void *, size_t),
   274                          void (*free)(void*),
   275                          size_t elsize, size_t max_items,
   276                          int (*compare)(const void *a, const void *b,
   277                                         void *udata),
   278                          void *udata)
   279  {
   280      _malloc = _malloc ? _malloc : malloc;
   281      _free = _free ? _free : free;
   282      if (max_items == 0) {
   283          max_items = 256;
   284      } else {
   285          if (max_items % 2 == 1) max_items--;
   286          if (max_items < 4) max_items = 4;
   287          if (max_items > 4096) max_items = 4096;
   288      }
   289      if (elsize == 0) panic("elsize is zero");
   290      if (compare == NULL) panic("compare is null");
   291      struct btree *btree = _malloc(sizeof(struct btree));
   292      if (!btree) {
   293          return NULL;
   294      }
   295      memset(btree, 0, sizeof(struct btree));
   296      int nspares = sizeof(btree->spares)/sizeof(void*);
   297      for (int i = 0; i < nspares; i++) {
   298          btree->spares[i] = _malloc(elsize);
   299          if (!btree->spares[i]) {
   300              for (i = 0; i < nspares; i++) {
   301                  if (btree->spares[i]) {
   302                      _free(btree->spares[i]);
   303                  }
   304              }
   305              _free(btree);
   306              return NULL;
   307          }
   308      }
   309      btree->_compare = compare;
   310      btree->max_items = max_items;
   311      btree->min_items = btree->max_items*40/100;
   312      btree->elsize = elsize;
   313      btree->udata = udata;
   314      btree->malloc = _malloc;
   315      btree->free = _free;
   316      return btree;
   317  }
   318  
   319  static void release_pool(struct btree *btree) {
   320      for (size_t i = 0; i < btree->pool.leaves.len; i++) {
   321          btree->free(btree->pool.leaves.nodes[i]);
   322      }
   323      btree->free(btree->pool.leaves.nodes);
   324      for (size_t i = 0; i < btree->pool.branches.len; i++) {
   325          btree->free(btree->pool.branches.nodes[i]);
   326      }
   327      btree->free(btree->pool.branches.nodes);
   328      memset(&btree->pool, 0, sizeof(struct pool));
   329  }
   330  
   331  // btree_free frees the btree. The items in the btree are not touched, so if
   332  // you need to free those then do so prior to calling this function.
   333  void btree_free(struct btree *btree) {
   334      if (btree->root) {
   335          node_free(btree, btree->root);
   336      }
   337      release_pool(btree);
   338      int nspares = sizeof(btree->spares)/sizeof(void*);
   339      for (int i = 0; i < nspares; i++) {
   340          btree->free(btree->spares[i]);
   341      }
   342      btree->free(btree);
   343  }
   344  
   345  static void reset_load_fields(struct btree *btree) {
   346      btree->litem = NULL;
   347      btree->lnode = NULL;
   348  }
   349  
   350  // btree_height returns the height of the btree.
   351  size_t btree_height(struct btree *btree) {
   352      return btree->height;
   353  }
   354  
   355  // btree_count returns the number of items in the btree.
   356  size_t btree_count(struct btree *btree) {
   357      return btree->count;
   358  }
   359  
   360  static void node_split(struct btree *btree, struct node *node,
   361                         struct node **right, void **median, bool lean_left)
   362  {
   363      int mid;
   364      if (lean_left) {
   365          // Split so the left node has as many items as possible, leaving the
   366          // new right with the minimum items. This makes more space available to
   367          // the right node for sequential inserts and bulk loading.
   368          mid = (int)(btree->max_items-1-btree->min_items);
   369          int mdif = (node->num_items-(mid+1))-(int)btree->min_items;
   370          if (mdif < 0) {
   371              mid += mdif;
   372          }
   373      } else {
   374          // split so that both left and right have the same number of items.
   375          mid = (int)(btree->max_items-1)/2;
   376      }
   377      *median = get_item_at(btree->elsize, node, (size_t)mid);
   378      *right = node->leaf ? gimme_leaf(btree) : gimme_branch(btree);
   379      (*right)->leaf = node->leaf;
   380      (*right)->num_items = node->num_items-((short)mid+1);
   381      memmove((*right)->items,
   382              node->items+(int)btree->elsize*(mid+1),
   383              (size_t)(*right)->num_items*btree->elsize);
   384      if (!node->leaf) {
   385          for (int i = 0; i <= (*right)->num_items; i++) {
   386              (*right)->children[i] = node->children[mid+1+i];
   387          }
   388      }
   389      node->num_items = (short)mid;
   390  }
   391  
   392  static int node_find(struct btree *btree, struct node *node, void *key,
   393                       bool *found, uint64_t *hint, int depth)
   394  {
   395      int low = 0;
   396      int high = node->num_items-1;
   397      if (hint && depth < 8) {
   398          int index = ((uint8_t*)hint)[depth];
   399          if (index > 0) {
   400              if (index > node->num_items-1) {
   401                  index = node->num_items-1;
   402              }
   403              void *item = get_item_at(btree->elsize, node, (size_t)index);
   404              int cmp = btcompare(btree, key, item);
   405              if (cmp == 0) {
   406                  *found = true;
   407                  return index;
   408              }
   409              if (cmp > 0) {
   410                  low = index+1;
   411              } else {
   412                  high = index-1;
   413              }
   414          }
   415      }
   416      int index;
   417      while ( low <= high ) {
   418          int mid = (low + high) / 2;
   419          void *item = get_item_at(btree->elsize, node, (size_t)mid);
   420          int cmp = btcompare(btree, key, item);
   421          if (cmp == 0) {
   422              *found = true;
   423              index = mid;
   424              goto done;
   425          }
   426          if (cmp < 0) {
   427              high = mid - 1;
   428          } else {
   429              low = mid + 1;
   430          }
   431      }
   432      *found = false;
   433      index = low;
   434  done:
   435      if (hint && depth < 8) {
   436          ((uint8_t*)hint)[depth] = (uint8_t)index;
   437      }
   438      return index;
   439  }
   440  
   441  static bool node_set(struct btree *btree, struct node *node, void *item,
   442                       bool lean_left, uint64_t *hint, int depth)
   443  {
   444      bool found = false;
   445      int i = node_find(btree, node, item, &found, hint, depth);
   446      if (found) {
   447          swap_item_at(btree->elsize, node, (size_t)i, item, btree->spares[0]);
   448          return true;
   449      }
   450      if (node->leaf) {
   451          node_shift_right(btree->elsize, node, (size_t)i);
   452          set_item_at(btree->elsize, node, (size_t)i, item);
   453          return false;
   454      }
   455      if (node_set(btree, node->children[i], item, lean_left, hint, depth+1)) {
   456          return true;
   457      }
   458      if ((size_t)node->children[i]->num_items == (btree->max_items-1)) {
   459          void *median = NULL;
   460          struct node *right = NULL;
   461          node_split(btree, node->children[i], &right, &median, lean_left);
   462          node_shift_right(btree->elsize, node, (size_t)i);
   463          set_item_at(btree->elsize, node, (size_t)i, median);
   464          node->children[i+1] = right;
   465      }
   466      return false;
   467  }
   468  
   469  static void *btree_set_x(struct btree *btree, void *item, bool lean_left,
   470                           uint64_t *hint)
   471  {
   472      reset_load_fields(btree);
   473  
   474      if (!item) {
   475          panic("item is null");
   476      }
   477  
   478      btree->oom = false;
   479      if (!fill_pool(btree)) {
   480          btree->oom = true;
   481          return NULL;
   482      }
   483      if (!btree->root) {
   484          btree->root = gimme_leaf(btree);
   485          set_item_at(btree->elsize, btree->root, 0, item);
   486          btree->root->num_items = 1;
   487          btree->count++;
   488          btree->height++;
   489          return NULL;
   490      }
   491      if (node_set(btree, btree->root, item, lean_left, hint, 0)) {
   492          return btree->spares[0];
   493      }
   494      btree->count++;
   495      if ((size_t)btree->root->num_items == (btree->max_items-1)) {
   496          void *old_root = btree->root;
   497          struct node *right = NULL;
   498          void *median = NULL;
   499          node_split(btree, old_root, &right, &median, lean_left);
   500          btree->root = gimme_branch(btree);
   501          btree->root->children[0] = old_root;
   502          set_item_at(btree->elsize, btree->root, 0, median);
   503          btree->root->children[1] = right;
   504          btree->root->num_items = 1;
   505          btree->height++;
   506      }
   507      return NULL;
   508  }
   509  
   510  // btree_set inserts or replaces an item in the btree. If an item is replaced
   511  // then it is returned otherwise NULL is returned.
   512  // The `btree_set`, `btree_set_hint`, and `btree_load` are the only btree
   513  // operations that allocates memory. If the system could not allocate the
   514  // memory then NULL is returned and btree_oom() returns true.
   515  void *btree_set(struct btree *btree, void *item) {
   516      return btree_set_x(btree, item, false, NULL);
   517  }
   518  
   519  // btree_set_hint is the same as btree_set except that an optional "hint" can
   520  // be provided which may make the operation quicker when done as a batch or
   521  // in a userspace context.
   522  // The `btree_set`, `btree_set_hint`, and `btree_load` are the only btree
   523  // operations that allocates memory. If the system could not allocate the
   524  // memory then NULL is returned and btree_oom() returns true.
   525  void *btree_set_hint(struct btree *btree, void *item, uint64_t *hint) {
   526      return btree_set_x(btree, item, false, hint);
   527  }
   528  
   529  // btree_load is the same as btree_set but is optimized for sequential bulk
   530  // loading. It can be up to 10x faster than btree_set when the items are
   531  // in exact order, but up to 25% slower when not in exact order.
   532  // The `btree_set`, `btree_set_hint`, and `btree_load` are the only btree
   533  // operations that allocates memory. If the system could not allocate the
   534  // memory then NULL is returned and btree_oom() returns true.
   535  void *btree_load(struct btree *btree, void *item) {
   536      if (!item) {
   537          panic("item is null");
   538      }
   539      if (btree->litem &&
   540          btree->lnode &&
   541          (size_t)btree->lnode->num_items < btree->max_items-2 &&
   542          btcompare(btree, item, btree->litem) > 0)
   543      {
   544          set_item_at(btree->elsize, btree->lnode,
   545                      (size_t)btree->lnode->num_items, item);
   546          btree->lnode->num_items++;
   547          btree->count++;
   548          btree->oom = false;
   549          return NULL;
   550      }
   551      void *prev = btree_set_x(btree, item, true, NULL);
   552      if (prev) {
   553          return prev;
   554      }
   555      struct node *node = btree->root;
   556      for (;;) {
   557          if (node->leaf) {
   558              btree->lnode = node;
   559              btree->litem = get_item_at(btree->elsize, node,
   560                                         (size_t)(node->num_items-1));
   561              break;
   562          }
   563          node = node->children[node->num_items];
   564      }
   565      return NULL;
   566  }
   567  
   568  // btree_get_hint is the same as btree_get except that an optional "hint" can
   569  // be provided which may make the operation quicker when done as a batch or
   570  // in a userspace context.
   571  void *btree_get_hint(struct btree *btree, void *key, uint64_t *hint) {
   572      struct node *node = btree->root;
   573      if (!node) {
   574          return NULL;
   575      }
   576      size_t elsz = btree->elsize;
   577      for (int depth = 0;;depth++) {
   578          bool found = false;
   579          int i = node_find(btree, node, key, &found, hint, depth);
   580          if (found) {
   581              return get_item_at(elsz, node, (size_t)i);
   582          }
   583          if (node->leaf) {
   584              return NULL;
   585          }
   586          node = node->children[i];
   587      }
   588  }
   589  
   590  // btree_get returns the item based on the provided key. If the item is not
   591  // found then NULL is returned.
   592  void *btree_get(struct btree *btree, void *key) {
   593      return btree_get_hint(btree, key, NULL);
   594  }
   595  
   596  enum delact {
   597      DELKEY, POPFRONT, POPBACK, POPMAX,
   598  };
   599  
   600  static bool node_delete(struct btree *btree, struct node *node, enum delact act,
   601                          size_t index, void *key, void *prev, uint64_t *hint,
   602                          int depth)
   603  {
   604      int i = 0;
   605      bool found = false;
   606      switch (act) {
   607      case POPMAX:
   608          i = node->num_items-1;
   609          found = true;
   610          break;
   611      case POPFRONT:
   612          i = 0;
   613          found = node->leaf;
   614          break;
   615      case POPBACK:
   616          if (!node->leaf) {
   617              i = node->num_items;
   618              found = false;
   619          } else {
   620              i = node->num_items-1;
   621              found = true;
   622          }
   623          break;
   624      case DELKEY:
   625          i = node_find(btree, node, key, &found, hint, depth);
   626          break;
   627      }
   628      if (node->leaf) {
   629          if (found) {
   630              // item was found in leaf, copy its contents and delete it.
   631              copy_item_into(btree->elsize, node, (size_t)i, prev);
   632              node_shift_left(btree->elsize, node, (size_t)i, false);
   633              return true;
   634          }
   635          return false;
   636      }
   637      // branch
   638      bool deleted = false;
   639      if (found) {
   640          if (act == POPMAX) {
   641              // popping off the max item into into its parent branch to maintain
   642              // a balanced tree.
   643              i++;
   644              node_delete(btree, node->children[i], POPMAX, 0, NULL,  prev, hint,
   645                          depth+1);
   646              deleted = true;
   647          } else {
   648              // item was found in branch, copy its contents, delete it, and
   649              // begin popping off the max items in child nodes.
   650              copy_item_into(btree->elsize, node, (size_t)i, prev);
   651              node_delete(btree, node->children[i], POPMAX, 0, NULL,
   652                          btree->spares[2], hint, depth+1);
   653              set_item_at(btree->elsize, node, (size_t)i, btree->spares[2]);
   654              deleted = true;
   655          }
   656      } else {
   657          // item was not found in this branch, keep searching.
   658          deleted = node_delete(btree, node->children[i], act, index, key, prev,
   659                                hint, depth+1);
   660      }
   661      if (!deleted) {
   662          return false;
   663      }
   664  
   665      if ((size_t)node->children[i]->num_items >= btree->min_items) {
   666          return true;
   667      }
   668  
   669      if (i == node->num_items) {
   670          i--;
   671      }
   672  
   673      struct node *left = node->children[i];
   674      struct node *right = node->children[i+1];
   675  
   676      if ((size_t)(left->num_items + right->num_items + 1) <
   677          (btree->max_items-1))
   678      {
   679          // merge left + item + right
   680          copy_item(btree->elsize, left, (size_t)left->num_items, node,
   681                    (size_t)i);
   682          left->num_items++;
   683          node_join(btree->elsize, left, right);
   684          takeaway(btree, right);
   685          node_shift_left(btree->elsize, node, (size_t)i, true);
   686      } else if (left->num_items > right->num_items) {
   687          // move left -> right
   688          node_shift_right(btree->elsize, right, 0);
   689          copy_item(btree->elsize, right, 0, node, (size_t)i);
   690          if (!left->leaf) {
   691              right->children[0] = left->children[left->num_items];
   692          }
   693          copy_item(btree->elsize, node, (size_t)i, left,
   694                    (size_t)(left->num_items-1));
   695          if (!left->leaf) {
   696              left->children[left->num_items] = NULL;
   697          }
   698          left->num_items--;
   699      } else {
   700          // move right -> left
   701          copy_item(btree->elsize, left, (size_t)left->num_items, node, (size_t)i);
   702          if (!left->leaf) {
   703              left->children[left->num_items+1] = right->children[0];
   704          }
   705          left->num_items++;
   706          copy_item(btree->elsize, node, (size_t)i, right, 0);
   707          node_shift_left(btree->elsize, right, 0, false);
   708      }
   709      return deleted;
   710  }
   711  
   712  static void *delete_x(struct btree *btree, enum delact act, size_t index,
   713                        void *key, uint64_t *hint)
   714  {
   715      reset_load_fields(btree);
   716  
   717      if (!btree->root) {
   718          return NULL;
   719      }
   720      bool deleted = node_delete(btree, btree->root, act, index, key,
   721                                 btree->spares[0], hint, 0);
   722      if (!deleted) {
   723          return NULL;
   724      }
   725      if (btree->root->num_items == 0) {
   726          struct node *old_root = btree->root;
   727          if (!btree->root->leaf) {
   728              btree->root = btree->root->children[0];
   729          } else {
   730              btree->root = NULL;
   731          }
   732          takeaway(btree, old_root);
   733          btree->height--;
   734      }
   735      btree->count--;
   736      return btree->spares[0];
   737  }
   738  
   739  // btree_delete_hint is the same as btree_delete except that an optional "hint"
   740  // can be provided which may make the operation quicker when done as a batch or
   741  // in a userspace context.
   742  void *btree_delete_hint(struct btree *btree, void *key, uint64_t *hint) {
   743      if (!key) panic("key is null");
   744      return delete_x(btree, DELKEY, 0, key, hint);
   745  }
   746  
   747  // btree_delete removes an item from the B-tree and returns it. If the item is
   748  // not found then NULL is returned.
   749  void *btree_delete(struct btree *btree, void *key) {
   750      return btree_delete_hint(btree, key, NULL);
   751  }
   752  
   753  // btree_pop_min removed the minimum value
   754  void *btree_pop_min(struct btree *btree) {
   755      return delete_x(btree, POPFRONT, 0, NULL, NULL);
   756  }
   757  
   758  // btree_pop_max removes the maximum value
   759  void *btree_pop_max(struct btree *btree) {
   760      return delete_x(btree, POPBACK, 0, NULL, NULL);
   761  }
   762  
   763  // btree_min returns the minimum value
   764  void *btree_min(struct btree *btree) {
   765      struct node *node = btree->root;
   766      if (!node) {
   767          return NULL;
   768      }
   769      for (;;) {
   770          if (node->leaf) {
   771              return get_item_at(btree->elsize, node, 0);
   772          }
   773          node = node->children[0];
   774      }
   775  }
   776  
   777  // btree_max returns the maximum value
   778  void *btree_max(struct btree *btree) {
   779      struct node *node = btree->root;
   780      if (!node) {
   781          return NULL;
   782      }
   783      for (;;) {
   784          if (node->leaf) {
   785              return get_item_at(btree->elsize, node,
   786                                 (size_t)(node->num_items-1));
   787          }
   788          node = node->children[node->num_items];
   789      }
   790  }
   791  
   792  
   793  static bool node_scan(struct btree *btree, struct node *node,
   794                        bool (*iter)(const void *item, void *udata),
   795                        void *udata)
   796  {
   797      if (node->leaf) {
   798          for (int i = 0; i < node->num_items; i++) {
   799              if (!iter(get_item_at(btree->elsize, node, (size_t)i), udata)) {
   800                  return false;
   801              }
   802          }
   803          return true;
   804      }
   805      for (int i = 0; i < node->num_items; i++) {
   806          if (!node_scan(btree, node->children[i], iter, udata)) {
   807              return false;
   808          }
   809          if (!iter(get_item_at(btree->elsize, node, (size_t)i), udata)) {
   810              return false;
   811          }
   812      }
   813      return node_scan(btree, node->children[node->num_items], iter, udata);
   814  }
   815  
   816  static bool node_ascend(struct btree *btree, struct node *node, void *pivot,
   817                          bool (*iter)(const void *item, void *udata),
   818                          void *udata, uint64_t *hint, int depth)
   819  {
   820      bool found;
   821      int i = node_find(btree, node, pivot, &found, hint, depth);
   822      if (!found) {
   823          if (!node->leaf) {
   824              if (!node_ascend(btree, node->children[i], pivot, iter, udata,
   825                               hint, depth+1))
   826              {
   827                  return false;
   828              }
   829          }
   830      }
   831      for (; i < node->num_items; i++) {
   832          if (!iter(get_item_at(btree->elsize, node, (size_t)i), udata)) {
   833              return false;
   834          }
   835          if (!node->leaf) {
   836              if (!node_scan(btree, node->children[i+1], iter, udata)) {
   837                  return false;
   838              }
   839          }
   840      }
   841      return true;
   842  }
   843  
   844  static bool node_reverse(struct btree *btree, struct node *node,
   845                           bool (*iter)(const void *item, void *udata),
   846                           void *udata)
   847  {
   848      if (node->leaf) {
   849  		for (int i = node->num_items - 1; i >= 0; i--) {
   850  			if (!iter(get_item_at(btree->elsize, node, (size_t)i), udata)) {
   851  				return false;
   852  			}
   853  		}
   854  		return true;
   855  	}
   856  	if (!node_reverse(btree, node->children[node->num_items], iter, udata)) {
   857  		return false;
   858  	}
   859  	for (int i = node->num_items - 1; i >= 0; i--) {
   860  		if (!iter(get_item_at(btree->elsize, node, (size_t)i), udata)) {
   861  			return false;
   862  		}
   863          if (!node_reverse(btree, node->children[i], iter, udata)) {
   864  			return false;
   865  		}
   866  	}
   867  	return true;
   868  }
   869  
   870  static bool node_descend(struct btree *btree, struct node *node, void *pivot,
   871                          bool (*iter)(const void *item, void *udata),
   872                          void *udata, uint64_t *hint, int depth)
   873  {
   874      bool found;
   875      int i = node_find(btree, node, pivot, &found, hint, depth);
   876      if (!found) {
   877          if (!node->leaf) {
   878              if (!node_descend(btree, node->children[i], pivot, iter, udata,
   879                                hint, depth+1))
   880              {
   881                  return false;
   882              }
   883          }
   884          i--;
   885      }
   886      for (; i >= 0; i--) {
   887          if (!iter(get_item_at(btree->elsize, node,(size_t)i), udata)) {
   888              return false;
   889          }
   890          if (!node->leaf) {
   891              if (!node_reverse(btree, node->children[i], iter, udata)) {
   892                  return false;
   893              }
   894          }
   895      }
   896      return true;
   897  }
   898  
   899  // btree_ascend_hint is the same as btree_ascend except that an optional
   900  // "hint" can be provided which may make the operation quicker when done as a
   901  // batch or in a userspace context.
   902  bool btree_ascend_hint(struct btree *btree, void *pivot,
   903                         bool (*iter)(const void *item, void *udata),
   904                         void *udata, uint64_t *hint)
   905  {
   906      if (btree->root) {
   907          if (!pivot) {
   908              return node_scan(btree, btree->root, iter, udata);
   909          }
   910          return node_ascend(btree, btree->root, pivot, iter, udata, hint, 0);
   911      }
   912      return true;
   913  }
   914  
   915  // Ascend the tree within the range [pivot, last]. In other words
   916  // `btree_ascend()` iterates over all items that are greater-than-or-equal-to
   917  // pivot in ascending order.
   918  // Param `pivot` can be NULL, which means all items are iterated over.
   919  // Param `iter` can return false to stop iteration early.
   920  // Returns false if the iteration has been stopped early.
   921  bool btree_ascend(struct btree *btree, void *pivot,
   922                    bool (*iter)(const void *item, void *udata), void *udata)
   923  {
   924      return btree_ascend_hint(btree, pivot, iter, udata, NULL);
   925  }
   926  
   927  // btree_descend_hint is the same as btree_descend except that an optional
   928  // "hint" can be provided which may make the operation quicker when done as a
   929  // batch or in a userspace context.
   930  bool btree_descend_hint(struct btree *btree, void *pivot,
   931                          bool (*iter)(const void *item, void *udata),
   932                          void *udata, uint64_t *hint)
   933  {
   934      if (btree->root) {
   935          if (!pivot) {
   936              return node_reverse(btree, btree->root, iter, udata);
   937          }
   938          return node_descend(btree, btree->root, pivot, iter, udata, hint, 0);
   939      }
   940      return true;
   941  }
   942  
   943  // Decend the tree within the range [pivot, first]. In other words
   944  // `btree_descend()` iterates over all items that are less-than-or-equal-to
   945  // pivot in descending order.
   946  // Param `pivot` can be NULL, which means all items are iterated over.
   947  // Param `iter` can return false to stop iteration early.
   948  // Returns false if the iteration has been stopped early.
   949  bool btree_descend(struct btree *btree, void *pivot,
   950                     bool (*iter)(const void *item, void *udata), void *udata)
   951  {
   952      return btree_descend_hint(btree, pivot, iter, udata, NULL);
   953  }
   954  
   955  #define BTSTOP      0
   956  #define BTCONTINUE  1
   957  #define BTSTARTOVER 2
   958  
   959  static int node_action_ascend(struct btree *btree, struct node *node,
   960                                void **pivot,
   961                                enum btree_action (*iter)(void *item,
   962                                                          void *udata),
   963                                void *udata, uint64_t *hint, int depth)
   964  {
   965      bool found = false;
   966      int i = 0;
   967      if (*pivot) {
   968          i = node_find(btree, node, *pivot, &found, hint, depth);
   969      }
   970      for (; i < node->num_items; i++) {
   971          if (!node->leaf) {
   972              int ret = node_action_ascend(btree, node->children[i], pivot, iter,
   973                                           udata, hint, depth+1);
   974              if (ret != BTCONTINUE) {
   975                  return ret;
   976              }
   977          }
   978          copy_item_into(btree->elsize, node, (size_t)i, btree->spares[0]);
   979          switch (iter(btree->spares[0], udata)) {
   980          case BTREE_NONE:
   981              break;
   982          case BTREE_DELETE:
   983              if (node->leaf && (size_t)node->num_items > btree->min_items) {
   984                  // delete in place
   985                  node_shift_left(btree->elsize, node, (size_t)i, false);
   986                  btree->count--;
   987                  i--;
   988                  break;
   989              } else {
   990                  // rebalancing is required, go the slow route
   991                  copy_item_into(btree->elsize, node, (size_t)i, btree->spares[1]);
   992                  btree_delete(btree, btree->spares[1]);
   993                  *pivot = btree->spares[1];
   994                  return BTSTARTOVER;
   995              }
   996          case BTREE_UPDATE: {
   997              void *item = get_item_at(btree->elsize, node, (size_t)i);
   998              if (btcompare(btree, item, btree->spares[0])) {
   999                  // Item keys have diverged. This is not fatal, but we need to
  1000                  // retry the operation until we get the response we're looking
  1001                  // for. There is a risk that a user, who does not understand
  1002                  // that the updated item must match exactly with the previous
  1003                  // item (ie "compare(a, b) == 0") , might create an infinite
  1004                  // loop like scenario.
  1005                  i--;
  1006              } else {
  1007                  // Item keys match, update memory and move on.
  1008                  set_item_at(btree->elsize, node, (size_t)i, btree->spares[0]);
  1009              }
  1010              break;
  1011          }
  1012          case BTREE_STOP:
  1013              return BTSTOP;
  1014          }
  1015      }
  1016      if (!node->leaf) {
  1017          int ret = node_action_ascend(btree, node->children[i], pivot, iter,
  1018                                       udata, hint, depth+1);
  1019          if (ret != BTCONTINUE) {
  1020              return ret;
  1021          }
  1022      }
  1023      return BTCONTINUE;
  1024  }
  1025  
  1026  static int node_action_descend(struct btree *btree, struct node *node,
  1027                                 void **pivot,
  1028                                 enum btree_action (*iter)(void *item,
  1029                                                           void *udata),
  1030                                 void *udata, uint64_t *hint, int depth)
  1031  {
  1032      bool found = false;
  1033      int i = node->num_items;
  1034      if (*pivot) {
  1035          i = node_find(btree, node, *pivot, &found, hint, depth);
  1036      }
  1037      if (!node->leaf && !found) {
  1038          int ret = node_action_descend(btree, node->children[i], pivot, iter,
  1039                                          udata, hint, depth+1);
  1040          if (ret != BTCONTINUE) {
  1041              return ret;
  1042          }
  1043      }
  1044      if (!found) {
  1045          i--;
  1046      }
  1047      for (;i >= 0;i--) {
  1048          copy_item_into(btree->elsize, node, (size_t)i, btree->spares[0]);
  1049          switch (iter(btree->spares[0], udata)) {
  1050          case BTREE_NONE:
  1051              break;
  1052          case BTREE_DELETE:
  1053              if (node->leaf && (size_t)node->num_items > btree->min_items) {
  1054                  // delete in place
  1055                  node_shift_left(btree->elsize, node, (size_t)i, false);
  1056                  btree->count--;
  1057                  // i++;
  1058                  break;
  1059              } else {
  1060                  // rebalancing is required, go the slow route
  1061                  copy_item_into(btree->elsize, node, (size_t)i, btree->spares[1]);
  1062                  btree_delete(btree, btree->spares[1]);
  1063                  *pivot = btree->spares[1];
  1064                  return BTSTARTOVER;
  1065              }
  1066          case BTREE_UPDATE: {
  1067              void *item = get_item_at(btree->elsize, node, (size_t)i);
  1068              if (btcompare(btree, item, btree->spares[0])) {
  1069                  // Item keys have diverged. This is not fatal, but we need to
  1070                  // retry the operation until we get the response we're looking
  1071                  // for. There is a risk that a user, who does not understand
  1072                  // that the updated item must match exactly with the previous
  1073                  // item (ie "compare(a, b) == 0") , might create an infinite
  1074                  // loop like scenario.
  1075                  i++;
  1076              } else {
  1077                  // Item keys match, update memory and move on.
  1078                  set_item_at(btree->elsize, node, (size_t)i, btree->spares[0]);
  1079              }
  1080              break;
  1081          }
  1082          case BTREE_STOP:
  1083              return BTSTOP;
  1084          }
  1085          if (!node->leaf) {
  1086              int ret = node_action_descend(btree, node->children[i], pivot, iter,
  1087                                            udata, hint, depth+1);
  1088              if (ret != BTCONTINUE) {
  1089                  return ret;
  1090              }
  1091          }
  1092      }
  1093      return BTCONTINUE;
  1094  }
  1095  
  1096  
  1097  // btree_action_ascend_hint is the same as btree_action_ascend but accepts
  1098  // and optional hint param.
  1099  void btree_action_ascend_hint(struct btree *btree, void *pivot,
  1100                                enum btree_action (*iter)(void *item,
  1101                                                          void *udata),
  1102                                void *udata, uint64_t *hint)
  1103  {
  1104      reset_load_fields(btree);
  1105      while (btree->root) {
  1106          int ret = node_action_ascend(btree, btree->root, &pivot, iter, udata,
  1107                                       hint, 0);
  1108          if (ret != BTSTARTOVER) {
  1109              break;
  1110          }
  1111      }
  1112  }
  1113  
  1114  // btree_action_ascend allows for making changes to items in the tree while
  1115  // iterating. It work just like btree_ascend except that the iterator is
  1116  // passed an item that can be optionally updated or deleted.
  1117  //
  1118  // To update an item, just make a change to the item and return BTREE_UPDATE.
  1119  // It's very important to not change the key equivalency of the item. In other
  1120  // words the original item and the new item must compare to zero using the
  1121  // comparator that was provided to btree_new(). Otherwise, the iterator will
  1122  // ignore the change and try the same item again.
  1123  //
  1124  // To delete an item, just return BTREE_DELETED.
  1125  // Return BTREE_NOTHING to make no change to the item or return BTREE_STOP to
  1126  // stop iterating.
  1127  void btree_action_ascend(struct btree *btree, void *pivot,
  1128                           enum btree_action (*iter)(void *item, void *udata),
  1129                           void *udata)
  1130  {
  1131      btree_action_ascend_hint(btree, pivot, iter, udata, NULL);
  1132  }
  1133  
  1134  // btree_action_descend_hint is the same as btree_action_descend but accepts
  1135  // and optional hint param.
  1136  void btree_action_descend_hint(struct btree *btree, void *pivot,
  1137                                 enum btree_action (*iter)(void *item,
  1138                                                          void *udata),
  1139                                 void *udata, uint64_t *hint)
  1140  {
  1141      reset_load_fields(btree);
  1142      while (btree->root) {
  1143          int ret = node_action_descend(btree, btree->root, &pivot, iter, udata,
  1144                                       hint, 0);
  1145          if (ret != BTSTARTOVER) {
  1146              break;
  1147          }
  1148      }
  1149  }
  1150  
  1151  // btree_action_descend allows for making changes to items in the tree while
  1152  // iterating. It work just like btree_descend except that the iterator is
  1153  // passed an item that can be optionally updated or deleted.
  1154  //
  1155  // To update an item, just make a change to the item and return BTREE_UPDATE.
  1156  // It's very important to not change the key equivalency of the item. In other
  1157  // words the original item and the new item must compare to zero using the
  1158  // comparator that was provided to btree_new(). Otherwise, the iterator will
  1159  // ignore the change and try the same item again.
  1160  //
  1161  // To delete an item, just return BTREE_DELETED.
  1162  // Return BTREE_NOTHING to make no change to the item or return BTREE_STOP to
  1163  // stop iterating.
  1164  void btree_action_descend(struct btree *btree, void *pivot,
  1165                           enum btree_action (*iter)(void *item, void *udata),
  1166                           void *udata)
  1167  {
  1168      btree_action_descend_hint(btree, pivot, iter, udata, NULL);
  1169  }
  1170  
  1171  
  1172  ////////////////////////////////////////////////////////////////////////////////
  1173  
  1174  static void node_print(struct btree *btree, struct node *node,
  1175                         void (*print)(void *), int depth)
  1176  {
  1177      if (node->leaf) {
  1178          for (int i = 0; i < depth; i++) {
  1179              printf("  ");
  1180          }
  1181          printf("[");
  1182          for (int i = 0; i < node->num_items; i++) {
  1183              if (i > 0) {
  1184                  printf(" ");
  1185              }
  1186              print(get_item_at(btree->elsize, node, (size_t)i));
  1187          }
  1188          printf("]\n");
  1189      } else {
  1190          for (short i = 0; i < node->num_items; i++) {
  1191              node_print(btree, node->children[i], print, depth+1);
  1192              for (int j = 0; j < depth; j++) {
  1193                  printf("  ");
  1194              }
  1195              print(get_item_at(btree->elsize, node, (size_t)i));
  1196              printf("\n");
  1197          }
  1198          node_print(btree, node->children[node->num_items], print, depth+1);
  1199      }
  1200  }
  1201  
  1202  void btree_print(struct btree *btree, void (*print)(void *item));
  1203  void btree_print(struct btree *btree, void (*print)(void *item)) {
  1204      if (btree->root) {
  1205          node_print(btree, btree->root, print, 0);
  1206      }
  1207  }
  1208  
  1209  // btree_oom returns true if the last btree_insert() call failed due to the
  1210  // system being out of memory.
  1211  bool btree_oom(struct btree *btree) {
  1212      return btree->oom;
  1213  }
  1214  
  1215  //==============================================================================
  1216  // TESTS AND BENCHMARKS
  1217  // $ cc -DBTREE_TEST btree.c && ./a.out              # run tests
  1218  // $ cc -DBTREE_TEST -O3 btree.c && BENCH=1 ./a.out  # run benchmarks
  1219  //==============================================================================
  1220  #ifdef BTREE_TEST
  1221  
  1222  // #ifdef __clang__
  1223  // #pragma clang diagnostic ignored "-Weverything"
  1224  // #endif
  1225  // #pragma GCC diagnostic ignored "-Wextra"
  1226  
  1227  
  1228  static void node_walk(struct btree *btree, struct node *node,
  1229                        void (*fn)(const void *item, void *udata), void *udata)
  1230  {
  1231      if (node->leaf) {
  1232          for (int i = 0; i < node->num_items; i++) {
  1233              fn(get_item_at(btree->elsize, node, i), udata);
  1234          }
  1235      } else {
  1236          for (int i = 0; i < node->num_items; i++) {
  1237              node_walk(btree, node->children[i], fn, udata);
  1238              fn(get_item_at(btree->elsize, node, i), udata);
  1239          }
  1240          node_walk(btree, node->children[node->num_items], fn, udata);
  1241      }
  1242  }
  1243  
  1244  // btree_walk visits every item in the tree.
  1245  static void btree_walk(struct btree *btree,
  1246                  void (*fn)(const void *item, void *udata), void *udata)
  1247  {
  1248      if (btree->root) {
  1249          node_walk(btree, btree->root, fn, udata);
  1250      }
  1251  }
  1252  
  1253  static size_t node_deepcount(struct node *node) {
  1254      size_t count = node->num_items;
  1255      if (!node->leaf) {
  1256          for (int i = 0; i <= node->num_items; i++) {
  1257              count += node_deepcount(node->children[i]);
  1258          }
  1259      }
  1260      return count;
  1261  }
  1262  
  1263  // btree_deepcount returns the number of items in the btree.
  1264  static size_t btree_deepcount(struct btree *btree) {
  1265      if (btree->root) {
  1266          return node_deepcount(btree->root);
  1267      }
  1268      return 0;
  1269  }
  1270  
  1271  static bool node_saneheight(struct node *node, int height, int maxheight) {
  1272      if (node->leaf) {
  1273          if (height != maxheight) {
  1274              return false;
  1275          }
  1276      } else {
  1277          int i = 0;
  1278          for (; i < node->num_items; i++) {
  1279              if (!node_saneheight(node->children[i], height+1, maxheight)) {
  1280                  return false;
  1281              }
  1282          }
  1283          if (!node_saneheight(node->children[i], height+1, maxheight)) {
  1284              return false;
  1285          }
  1286      }
  1287      return true;
  1288  }
  1289  
  1290  // btree_saneheight returns true if the height of all leaves match the height
  1291  // of the btree.
  1292  static bool btree_saneheight(struct btree *btree) {
  1293      if (btree->root) {
  1294          return node_saneheight(btree->root, 1, btree->height);
  1295      }
  1296      return true;
  1297  }
  1298  
  1299  static bool node_saneprops(struct btree *btree, struct node *node, int height) {
  1300      if (height == 1) {
  1301          if (node->num_items < 1 || node->num_items > btree->max_items) {
  1302              return false;
  1303          }
  1304      } else {
  1305          if (node->num_items < btree->min_items ||
  1306              node->num_items > btree->max_items)
  1307          {
  1308              return false;
  1309          }
  1310      }
  1311      if (!node->leaf) {
  1312          for (int i = 0; i < node->num_items; i++) {
  1313              if (!node_saneprops(btree, node->children[i], height+1)) {
  1314                  return false;
  1315              }
  1316          }
  1317          if (!node_saneprops(btree, node->children[node->num_items], height+1)) {
  1318              return false;
  1319          }
  1320      }
  1321      return true;
  1322  }
  1323  
  1324  
  1325  static bool btree_saneprops(struct btree *btree) {
  1326      if (btree->root) {
  1327          return node_saneprops(btree, btree->root, 1);
  1328      }
  1329      return true;
  1330  }
  1331  
  1332  struct sane_walk_ctx {
  1333      struct btree *btree;
  1334      const void *last;
  1335      size_t count;
  1336      bool bad;
  1337  };
  1338  
  1339  static void sane_walk(const void *item, void *udata) {
  1340      struct sane_walk_ctx *ctx = udata;
  1341      if (ctx->bad) {
  1342          return;
  1343      }
  1344      if (ctx->last != NULL) {
  1345          if (btcompare(ctx->btree, ctx->last, item) >= 0) {
  1346              ctx->bad = true;
  1347              return;
  1348          }
  1349      }
  1350      ctx->last = item;
  1351      ctx->count++;
  1352  }
  1353  
  1354  // btree_sane returns true if the entire btree and every node are valid.
  1355  // - height of all leaves are the equal to the btree height.
  1356  // - deep count matches the btree count.
  1357  // - all nodes have the correct number of items and counts.
  1358  // - all items are in order.
  1359  bool btree_sane(struct btree *btree) {
  1360      if (!btree_saneheight(btree)) {
  1361          fprintf(stderr, "!sane-height\n");
  1362          return false;
  1363      }
  1364      if (btree_deepcount(btree) != btree->count) {
  1365          fprintf(stderr, "!sane-count\n");
  1366          return false;
  1367      }
  1368      if (!btree_saneprops(btree)) {
  1369          fprintf(stderr, "!sane-props\n");
  1370          return false;
  1371      }
  1372      struct sane_walk_ctx ctx = { .btree = btree };
  1373      btree_walk(btree, sane_walk, &ctx);
  1374      if (ctx.bad || (ctx.count != btree->count)) {
  1375          fprintf(stderr, "!sane-order\n");
  1376          return false;
  1377      }
  1378      return true;
  1379  }
  1380  
  1381  struct slowget_at_ctx {
  1382      struct btree *btree;
  1383      int index;
  1384      int count;
  1385      void *result;
  1386  };
  1387  
  1388  static bool slowget_at_iter(const void *item, void *udata) {
  1389      struct slowget_at_ctx *ctx = udata;
  1390      if (ctx->count == ctx->index) {
  1391          ctx->result = (void*)item;
  1392          return false;
  1393      }
  1394      ctx->count++;
  1395      return true;
  1396  }
  1397  
  1398  void *btree_slowget_at(struct btree *btree, size_t index);
  1399  void *btree_slowget_at(struct btree *btree, size_t index) {
  1400      struct slowget_at_ctx ctx = { .btree = btree, .index = index };
  1401      btree_ascend(btree, NULL, slowget_at_iter, &ctx);
  1402      return ctx.result;
  1403  }
  1404  
  1405  
  1406  void print_int(void *item) {
  1407      printf("%d", *(int*)item);
  1408  }
  1409  
  1410  #include <stdlib.h>
  1411  #include <string.h>
  1412  #include <time.h>
  1413  #include <assert.h>
  1414  #include <stdio.h>
  1415  #include "btree.h"
  1416  
  1417  static bool rand_alloc_fail = false;
  1418  static int rand_alloc_fail_odds = 3; // 1 in 3 chance malloc will fail.
  1419  static uintptr_t total_allocs = 0;
  1420  static uintptr_t total_mem = 0;
  1421  
  1422  static void *xmalloc(size_t size) {
  1423      if (rand_alloc_fail && rand()%rand_alloc_fail_odds == 0) {
  1424          return NULL;
  1425      }
  1426      void *mem = malloc(sizeof(uintptr_t)+size);
  1427      assert(mem);
  1428      *(uintptr_t*)mem = size;
  1429      total_allocs++;
  1430      total_mem += size;
  1431      return (char*)mem+sizeof(uintptr_t);
  1432  }
  1433  
  1434  static void xfree(void *ptr) {
  1435      if (ptr) {
  1436          total_mem -= *(uintptr_t*)((char*)ptr-sizeof(uintptr_t));
  1437          free((char*)ptr-sizeof(uintptr_t));
  1438          total_allocs--;
  1439      }
  1440  }
  1441  
  1442  static void shuffle(void *array, size_t numels, size_t elsize) {
  1443      char tmp[elsize];
  1444      char *arr = array;
  1445      for (size_t i = 0; i < numels - 1; i++) {
  1446          int j = i + rand() / (RAND_MAX / (numels - i) + 1);
  1447          memcpy(tmp, arr + j * elsize, elsize);
  1448          memcpy(arr + j * elsize, arr + i * elsize, elsize);
  1449          memcpy(arr + i * elsize, tmp, elsize);
  1450      }
  1451  }
  1452  
  1453  static char nothing[] = "nothing";
  1454  
  1455  static int compare_ints_nudata(const void *a, const void *b) {
  1456      return *(int*)a - *(int*)b;
  1457  }
  1458  static int compare_ints(const void *a, const void *b, void *udata) {
  1459      assert(udata == nothing);
  1460      return *(int*)a - *(int*)b;
  1461  }
  1462  
  1463  struct iter_ctx {
  1464      bool rev;
  1465      struct btree *btree;
  1466      const void *last;
  1467      int count;
  1468      bool bad;
  1469  };
  1470  
  1471  static bool iter(const void *item, void *udata) {
  1472      struct iter_ctx *ctx = udata;
  1473      if (ctx->bad) {
  1474          return false;
  1475      }
  1476      if (ctx->last) {
  1477          if (ctx->rev) {
  1478              if (btcompare(ctx->btree, item, ctx->last) >= 0) {
  1479                  ctx->bad = true;
  1480                  return false;
  1481              }
  1482          } else {
  1483              if (btcompare(ctx->btree, ctx->last, item) >= 0) {
  1484                  ctx->bad = true;
  1485                  return false;
  1486              }
  1487          }
  1488      }
  1489      ctx->last = item;
  1490      ctx->count++;
  1491      return true;
  1492  }
  1493  
  1494  struct pair {
  1495      int key;
  1496      int val;
  1497  };
  1498  
  1499  static int compare_pairs_nudata(const void *a, const void *b) {
  1500      return ((struct pair*)a)->key - ((struct pair*)b)->key;
  1501  }
  1502  
  1503  static int compare_pairs(const void *a, const void *b, void *udata) {
  1504      assert(udata == nothing);
  1505      return ((struct pair*)a)->key - ((struct pair*)b)->key;
  1506  }
  1507  
  1508  struct pair_keep_ctx {
  1509      struct pair last;
  1510      int count;
  1511  };
  1512  
  1513  enum btree_action pair_keep(void *item, void *udata) {
  1514      struct pair_keep_ctx *ctx = udata;
  1515      if (ctx->count > 0) {
  1516          assert(compare_pairs_nudata(item, &ctx->last) > 0);
  1517      }
  1518      memcpy(&ctx->last, item, sizeof(struct pair));
  1519      ctx->count++;
  1520      return BTREE_NONE;
  1521  }
  1522  
  1523  enum btree_action pair_keep_desc(void *item, void *udata) {
  1524      struct pair_keep_ctx *ctx = udata;
  1525      // struct pair *pair = (struct pair *)item;
  1526      // if (ctx->count == 0) {
  1527      //     printf("((%d))\n", pair->key);
  1528      // }
  1529  
  1530      if (ctx->count > 0) {
  1531          assert(compare_pairs_nudata(item, &ctx->last) < 0);
  1532      }
  1533      memcpy(&ctx->last, item, sizeof(struct pair));
  1534      ctx->count++;
  1535      return BTREE_NONE;
  1536  }
  1537  
  1538  
  1539  enum btree_action pair_update(void *item, void *udata) {
  1540      ((struct pair*)item)->val++;
  1541      return BTREE_UPDATE;
  1542  }
  1543  
  1544  bool pair_update_check(const void *item, void *udata) {
  1545      int half = *(int*)udata;
  1546      struct pair *pair = (struct pair *)item;
  1547      if (pair->key < half) {
  1548          assert(pair->val == pair->key + 1);
  1549      } else {
  1550          assert(pair->val == pair->key + 2);
  1551      }
  1552      return true;
  1553  }
  1554  
  1555  bool pair_update_check_desc(const void *item, void *udata) {
  1556      int half = *(int*)udata;
  1557      struct pair *pair = (struct pair *)item;
  1558      if (pair->key > half) {
  1559          assert(pair->val == pair->key + 1);
  1560      } else {
  1561          assert(pair->val == pair->key + 2);
  1562      }
  1563      return true;
  1564  }
  1565  
  1566  enum btree_action pair_delete(void *item, void *udata) {
  1567      return BTREE_DELETE;
  1568  }
  1569  
  1570  
  1571  enum btree_action pair_cycle(void *item, void *udata) {
  1572      int i = *(int*)udata;
  1573      *(int*)udata = i+1;
  1574      switch (i % 3) {
  1575      case 0:
  1576          return BTREE_NONE;
  1577      case 1:
  1578          ((struct pair*)item)->val++;
  1579          return BTREE_UPDATE;
  1580      case 2:
  1581          return BTREE_DELETE;
  1582      }
  1583      panic("!");
  1584  }
  1585  
  1586  const int def_MAX_ITEMS = 6;
  1587  const int def_N = 5000;
  1588  
  1589  
  1590  static void test_action_ascend() {
  1591      int max_items = getenv("MAX_ITEMS")?atoi(getenv("MAX_ITEMS")):def_MAX_ITEMS;
  1592      int N = getenv("N")?atoi(getenv("N")):def_N;
  1593  
  1594      rand_alloc_fail = false;
  1595      assert(total_allocs == 0);
  1596  
  1597      struct pair *pairs = xmalloc(sizeof(struct pair) * N);
  1598      for (int i = 0; i < N; i++) {
  1599          pairs[i].key = i;
  1600          pairs[i].val = i;
  1601      }
  1602  
  1603      // qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1604  
  1605      struct btree *btree = btree_new(sizeof(struct pair), max_items,
  1606                                      compare_pairs, nothing);
  1607  
  1608      printf("== testing action ascend\n");
  1609      shuffle(pairs, N, sizeof(struct pair));
  1610      for (int i = 0; i < N; i++) {
  1611          btree_set(btree, &pairs[i]);
  1612      }
  1613      // test that all items exist and are in order, BTREE_NONE
  1614      struct pair_keep_ctx ctx = { 0 };
  1615      btree_action_ascend(btree, NULL, pair_keep, &ctx);
  1616      assert(ctx.count == N);
  1617      assert(btree_sane(btree));
  1618  
  1619      // test items exist at various pivot points and are in order, BTREE_NONE
  1620      qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1621      for (int i = 2 ; i < 16; i++) {
  1622          memset(&ctx, 0, sizeof(struct pair_keep_ctx));
  1623          btree_action_ascend(btree, &pairs[N/i], pair_keep, &ctx);
  1624          assert(ctx.count == N-N/i);
  1625          assert(btree_sane(btree));
  1626      }
  1627  
  1628      // update all item values, BTREE_UPDATE
  1629      btree_action_ascend(btree, NULL, pair_update, NULL);
  1630      btree_action_ascend(btree, &pairs[N/2], pair_update, NULL);
  1631      int half = N/2;
  1632      btree_ascend(btree, NULL, pair_update_check, &half);
  1633      assert(btree_sane(btree));
  1634  
  1635      // delete all items, BTREE_DELETE
  1636      btree_action_ascend(btree, NULL, pair_delete, NULL);
  1637      assert(btree_count(btree) == 0);
  1638      assert(btree_sane(btree));
  1639  
  1640      // delete items at various pivot points, BTREE_DELETE
  1641      for (int i = 2 ; i < 16; i++) {
  1642          qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1643          for (int i = 0; i < N; i++) {
  1644              btree_set(btree, &pairs[i]);
  1645          }
  1646          assert(btree_count(btree) == N);
  1647          btree_action_ascend(btree, &pairs[N/i], pair_delete, NULL);
  1648          assert(btree_count(btree) == N/i);
  1649          assert(btree_sane(btree));
  1650      }
  1651  
  1652  
  1653      qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1654      for (int i = 0; i < N; i++) {
  1655          btree_set(btree, &pairs[i]);
  1656      }
  1657  
  1658      // cycle the BTREE_NONE, BTREE_UPDATE, BTREE_DELETE
  1659      int cycle = 0;
  1660      btree_action_ascend(btree, NULL, pair_cycle, &cycle);
  1661      assert(btree_count(btree) == N-N/3);
  1662      assert(btree_sane(btree));
  1663      for (int i = 0; i < N; i++) {
  1664          struct pair *pair = btree_get(btree, &pairs[i]);
  1665          switch (i % 3) {
  1666          case 0:
  1667              assert(pair && pair->key == pair->val);
  1668              break;
  1669          case 1:
  1670              assert(pair && pair->key == pair->val-1);
  1671              break;
  1672          case 2:
  1673              assert(!pair);
  1674              break;
  1675          }
  1676      }
  1677  
  1678      printf("== testing action descend\n");
  1679      // do the same stuff as the ascend test, but in reverse
  1680      qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1681      for (int i = 0; i < N; i++) {
  1682          btree_set(btree, &pairs[i]);
  1683      }
  1684  
  1685      // test that all items exist and are in order, BTREE_NONE
  1686      memset(&ctx, 0, sizeof(struct pair_keep_ctx));
  1687      // printf(">>%d<<\n", pairs[N/2].key);
  1688      btree_action_descend(btree, NULL, pair_keep_desc, &ctx);
  1689      assert(ctx.count == N);
  1690      assert(btree_sane(btree));
  1691  
  1692      // test items exist at various pivot points and are in order, BTREE_NONE
  1693      qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1694      for (int i = 2 ; i < 16; i++) {
  1695          memset(&ctx, 0, sizeof(struct pair_keep_ctx));
  1696          btree_action_descend(btree, &pairs[N/i], pair_keep_desc, &ctx);
  1697          assert(ctx.count == N/i+1);
  1698          assert(btree_sane(btree));
  1699      }
  1700  
  1701      // update all item values, BTREE_UPDATE
  1702      btree_action_descend(btree, NULL, pair_update, NULL);
  1703      btree_action_descend(btree, &pairs[N/2], pair_update, NULL);
  1704      half = N/2;
  1705      btree_descend(btree, NULL, pair_update_check_desc, &half);
  1706      assert(btree_sane(btree));
  1707  
  1708      // delete all items, BTREE_DELETE
  1709      btree_action_descend(btree, NULL, pair_delete, NULL);
  1710      assert(btree_count(btree) == 0);
  1711      assert(btree_sane(btree));
  1712  
  1713      // delete items at various pivot points, BTREE_DELETE
  1714      for (int i = 2 ; i < 16; i++) {
  1715          qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1716          for (int i = 0; i < N; i++) {
  1717              btree_set(btree, &pairs[i]);
  1718          }
  1719          assert(btree_count(btree) == N);
  1720          btree_action_descend(btree, &pairs[N/i], pair_delete, NULL);
  1721          assert(btree_count(btree) == N-(N/i+1));
  1722          assert(btree_sane(btree));
  1723      }
  1724  
  1725      qsort(pairs, N, sizeof(struct pair), compare_pairs_nudata);
  1726      for (int i = 0; i < N; i++) {
  1727          btree_set(btree, &pairs[i]);
  1728      }
  1729  
  1730      // cycle the BTREE_NONE, BTREE_UPDATE, BTREE_DELETE
  1731      cycle = 0;
  1732      btree_action_descend(btree, NULL, pair_cycle, &cycle);
  1733      assert(btree_count(btree) == N-N/3);
  1734      assert(btree_sane(btree));
  1735      for (int i = N-1, j = 0; i >= 0; i--, j++) {
  1736          struct pair *pair = btree_get(btree, &pairs[i]);
  1737          switch (j % 3) {
  1738          case 0:
  1739              assert(pair && pair->key == pair->val);
  1740              break;
  1741          case 1:
  1742              assert(pair && pair->key == pair->val-1);
  1743              break;
  1744          case 2:
  1745              assert(!pair);
  1746              break;
  1747          }
  1748      }
  1749  
  1750      xfree(pairs);
  1751      btree_free(btree);
  1752  
  1753      if (total_allocs != 0) {
  1754          fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs);
  1755          exit(1);
  1756      }
  1757  }
  1758  
  1759  static void test_basic() {
  1760      int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
  1761      int max_items = getenv("MAX_ITEMS")?atoi(getenv("MAX_ITEMS")):def_MAX_ITEMS;
  1762      int N = getenv("N")?atoi(getenv("N")):def_N;
  1763      printf("seed=%d, max_items=%d, count=%d, item_size=%zu\n",
  1764          seed, max_items, N, sizeof(int));
  1765      srand(seed);
  1766  
  1767      assert(total_allocs == 0);
  1768      rand_alloc_fail = true;
  1769  
  1770  
  1771      printf("== testing basic operations\n");
  1772  
  1773      int *vals;
  1774      while(!(vals = xmalloc(sizeof(int) * N))){}
  1775  
  1776      for (int i = 0; i < N; i++) {
  1777          vals[i] = i*10;
  1778      }
  1779  
  1780      struct btree *btree = NULL;
  1781      for (int h = 0; h < 2; h++) {
  1782          if (btree) btree_free(btree);
  1783          while (!(btree = btree_new(sizeof(int), max_items, compare_ints,
  1784                                     nothing))){}
  1785  
  1786          shuffle(vals, N, sizeof(int));
  1787          uint64_t hint = 0;
  1788          uint64_t *hint_ptr = h == 0 ? NULL : &hint;
  1789  
  1790          for (int i = 0; i < N; i++) {
  1791              int *v;
  1792              v = btree_get_hint(btree, &vals[i], hint_ptr);
  1793              assert(!v);
  1794              while (true) {
  1795                  v = btree_set_hint(btree, &vals[i], hint_ptr);
  1796                  assert(!v);
  1797                  if (!btree_oom(btree)) {
  1798                      break;
  1799                  }
  1800              }
  1801              while (true) {
  1802                  v = btree_set_hint(btree, &vals[i], hint_ptr);
  1803                  if (!v) {
  1804                      assert(btree_oom(btree));
  1805                  } else {
  1806                      assert(v && *(int*)v == vals[i]);
  1807                      break;
  1808                  }
  1809              }
  1810              v = btree_get_hint(btree, &vals[i], hint_ptr);
  1811              assert(v && *(int*)v == vals[i]);
  1812              assert(btree_count(btree) == (size_t)(i+1));
  1813              assert(btree_sane(btree));
  1814  
  1815              // delete item
  1816              v = btree_delete_hint(btree, &vals[i], hint_ptr);
  1817              assert(v && *v == vals[i]);
  1818              assert(btree_count(btree) == (size_t)(i));
  1819              assert(btree_sane(btree));
  1820  
  1821              v = btree_get_hint(btree, &vals[i], hint_ptr);
  1822              assert(!v);
  1823  
  1824              // reinsert item
  1825              v = btree_set_hint(btree, &vals[i], hint_ptr);
  1826              assert(!v);
  1827              assert(btree_count(btree) == (size_t)(i+1));
  1828              assert(btree_sane(btree));
  1829  
  1830              v = btree_get_hint(btree, &vals[i], hint_ptr);
  1831              assert(v && *(int*)v == vals[i]);
  1832          }
  1833      }
  1834  
  1835      printf("== testing ascend\n");
  1836      {
  1837          // ascend
  1838          struct iter_ctx ctx = { .btree = btree, .rev = false };
  1839          bool ret = btree_ascend(btree, NULL, iter, &ctx);
  1840          assert(ret && !ctx.bad && ctx.count == N);
  1841  
  1842          for (int i = 0; i < N; i++) {
  1843              struct iter_ctx ctx = { .btree = btree, .rev = false };
  1844              bool ret = btree_ascend(btree, &(int){i*10}, iter, &ctx);
  1845              assert(ret && !ctx.bad && ctx.count == N-i);
  1846          }
  1847  
  1848          for (int i = 0; i < N; i++) {
  1849              struct iter_ctx ctx = { .btree = btree, .rev = false };
  1850              bool ret = btree_ascend(btree, &(int){i*10-1}, iter, &ctx);
  1851              assert(ret && !ctx.bad && ctx.count == N-i);
  1852          }
  1853  
  1854          for (int i = 0; i < N; i++) {
  1855              struct iter_ctx ctx = { .btree = btree, .rev = false };
  1856              bool ret = btree_ascend(btree, &(int){i*10+1}, iter, &ctx);
  1857              assert(ret && !ctx.bad && ctx.count == N-i-1);
  1858          }
  1859      }
  1860  
  1861      printf("== testing descend\n");
  1862      {
  1863          // decend
  1864          struct iter_ctx ctx = { .btree = btree, .rev = true };
  1865          bool ret = btree_descend(btree, NULL, iter, &ctx);
  1866          assert(ret && !ctx.bad && ctx.count == N);
  1867  
  1868          for (int i = N-1, j = 0; i >= 0; i--, j++) {
  1869              struct iter_ctx ctx = { .btree = btree, .rev = true };
  1870              bool ret = btree_descend(btree, &(int){i*10}, iter, &ctx);
  1871              assert(ret && !ctx.bad && ctx.count == N-(N-i)+1);
  1872          }
  1873  
  1874          for (int i = N-1; i >= 0; i--) {
  1875              struct iter_ctx ctx = { .btree = btree, .rev = true };
  1876              bool ret = btree_descend(btree, &(int){i*10+1}, iter, &ctx);
  1877              assert(ret && !ctx.bad && ctx.count == N-(N-i)+1);
  1878          }
  1879  
  1880          for (int i = N-1; i >= 0; i--) {
  1881              struct iter_ctx ctx = { .btree = btree, .rev = true };
  1882              bool ret = btree_descend(btree, &(int){i*10-1}, iter, &ctx);
  1883              assert(ret && !ctx.bad && ctx.count == N-(N-i));
  1884          }
  1885      }
  1886  
  1887  
  1888  
  1889      // delete all items
  1890      shuffle(vals, N, sizeof(int));
  1891      for (int i = 0; i < N; i++) {
  1892          int *v = btree_delete(btree, &vals[i]);
  1893          assert(v && *(int*)v == vals[i]);
  1894          assert(btree_sane(btree));
  1895      }
  1896  
  1897      printf("== testing pop-min\n");
  1898  
  1899      // reinsert
  1900      shuffle(vals, N, sizeof(int));
  1901      int min, max;
  1902      for (int i = 0; i < N; i++) {
  1903          int *v;
  1904          while (true) {
  1905              v = btree_set(btree, &vals[i]);
  1906              assert(!v);
  1907              if (!btree_oom(btree)) {
  1908                  break;
  1909              }
  1910          }
  1911          if (i == 0) {
  1912              min = vals[i], max = vals[i];
  1913          } else {
  1914              if (vals[i] < min) {
  1915                  min = vals[i];
  1916              } else if (vals[i] > max) {
  1917                  max = vals[i];
  1918              }
  1919          }
  1920          assert(btree_sane(btree));
  1921          v = btree_min(btree);
  1922          assert(v && *(int*)v == min);
  1923          v = btree_max(btree);
  1924          assert(v && *(int*)v == max);
  1925      }
  1926  
  1927      // pop-min
  1928      for (int i = 0; i < N; i++) {
  1929          int *v = btree_pop_min(btree);
  1930          assert(v && *(int*)v == i*10);
  1931          assert(btree_sane(btree));
  1932      }
  1933  
  1934      printf("== testing pop-max\n");
  1935      // reinsert
  1936      shuffle(vals, N, sizeof(int));
  1937      for (int i = 0; i < N; i++) {
  1938          while (true) {
  1939              assert(!btree_set(btree, &vals[i]));
  1940              if (!btree_oom(btree)) {
  1941                  break;
  1942              }
  1943          }
  1944      }
  1945  
  1946      // pop-max
  1947      for (int i = 0; i < N; i++) {
  1948          int *v = btree_pop_max(btree);
  1949          assert(v && *(int*)v == (N-i-1)*10);
  1950          assert(btree_sane(btree));
  1951      }
  1952  
  1953      btree_free(btree);
  1954      xfree(vals);
  1955  
  1956      if (total_allocs != 0) {
  1957          fprintf(stderr, "total_allocs: expected 0, got %lu\n", total_allocs);
  1958          exit(1);
  1959      }
  1960  }
  1961  
  1962  #define bench(name, N, code) {{ \
  1963      if (strlen(name) > 0) { \
  1964          printf("%-14s ", name); \
  1965      } \
  1966      size_t tmem = total_mem; \
  1967      size_t tallocs = total_allocs; \
  1968      uint64_t bytes = 0; \
  1969      clock_t begin = clock(); \
  1970      for (int i = 0; i < N; i++) { \
  1971          (code); \
  1972      } \
  1973      clock_t end = clock(); \
  1974      double elapsed_secs = (double)(end - begin) / CLOCKS_PER_SEC; \
  1975      double bytes_sec = (double)bytes/elapsed_secs; \
  1976      double ns_op = elapsed_secs/(double)N*1e9; \
  1977      if (ns_op < 10) { \
  1978          printf("%d ops in %.3f secs, %.1f ns/op, %.0f op/sec", \
  1979              N, elapsed_secs, ns_op, (double)N/elapsed_secs \
  1980          ); \
  1981      } else { \
  1982          printf("%d ops in %.3f secs, %.0f ns/op, %.0f op/sec", \
  1983              N, elapsed_secs, ns_op, (double)N/elapsed_secs \
  1984          ); \
  1985      } \
  1986      if (bytes > 0) { \
  1987          printf(", %.1f GB/sec", bytes_sec/1024/1024/1024); \
  1988      } \
  1989      if (total_mem > tmem) { \
  1990          size_t used_mem = total_mem-tmem; \
  1991          printf(", %.2f bytes/op", (double)used_mem/N); \
  1992      } \
  1993      if (total_allocs > tallocs) { \
  1994          size_t used_allocs = total_allocs-tallocs; \
  1995          printf(", %.2f allocs/op", (double)used_allocs/N); \
  1996      } \
  1997      printf("\n"); \
  1998  }}
  1999  
  2000  bool simple_iter(const void *item, void *udata) {
  2001      return true;
  2002  }
  2003  
  2004  enum btree_action del_asc_odds(void *item, void *udata) {
  2005      int count = *(int*)udata;
  2006      count++;
  2007      *(int*)udata = count;
  2008      if ((count & 1) == 1) {
  2009          return BTREE_DELETE;
  2010      } else {
  2011          return BTREE_NONE;
  2012      }
  2013  }
  2014  
  2015  static void benchmarks() {
  2016      int seed = getenv("SEED")?atoi(getenv("SEED")):time(NULL);
  2017      int max_items = getenv("MAX_ITEMS")?atoi(getenv("MAX_ITEMS")):256;
  2018      int N = getenv("N")?atoi(getenv("N")):1000000;
  2019      printf("seed=%d, max_items=%d, count=%d, item_size=%zu\n",
  2020          seed, max_items, N, sizeof(int));
  2021      srand(seed);
  2022  
  2023  
  2024      int *vals = xmalloc(N * sizeof(int));
  2025      for (int i = 0; i < N; i++) {
  2026          vals[i] = i;
  2027      }
  2028  
  2029      shuffle(vals, N, sizeof(int));
  2030  
  2031      struct btree *btree;
  2032      uint64_t hint = 0;
  2033  
  2034      btree = btree_new(sizeof(int), max_items, compare_ints, nothing);
  2035      qsort(vals, N, sizeof(int), compare_ints_nudata);
  2036      bench("load (seq)", N, {
  2037          btree_load(btree, &vals[i]);
  2038      })
  2039      btree_free(btree);
  2040  
  2041      shuffle(vals, N, sizeof(int));
  2042      btree = btree_new(sizeof(int), max_items, compare_ints, nothing);
  2043      bench("load (rand)", N, {
  2044          btree_set_hint(btree, &vals[i], &hint);
  2045      })
  2046      btree_free(btree);
  2047  
  2048  
  2049      btree = btree_new(sizeof(int), max_items, compare_ints, nothing);
  2050      qsort(vals, N, sizeof(int), compare_ints_nudata);
  2051      bench("set (seq)", N, {
  2052          btree_set(btree, &vals[i]);
  2053      })
  2054      btree_free(btree);
  2055  
  2056      ////
  2057      qsort(vals, N, sizeof(int), compare_ints_nudata);
  2058      btree = btree_new(sizeof(int), max_items, compare_ints, nothing);
  2059      bench("set (seq-hint)", N, {
  2060          btree_set_hint(btree, &vals[i], &hint);
  2061      })
  2062      btree_free(btree);
  2063  
  2064      ////
  2065      shuffle(vals, N, sizeof(int));
  2066      btree = btree_new(sizeof(int), max_items, compare_ints, nothing);
  2067      bench("set (rand)", N, {
  2068          btree_set(btree, &vals[i]);
  2069      })
  2070  
  2071  
  2072      qsort(vals, N, sizeof(int), compare_ints_nudata);
  2073      bench("get (seq)", N, {
  2074          btree_get(btree, &vals[i]);
  2075      })
  2076  
  2077      bench("get (seq-hint)", N, {
  2078          btree_get_hint(btree, &vals[i], &hint);
  2079      })
  2080  
  2081      shuffle(vals, N, sizeof(int));
  2082      bench("get (rand)", N, {
  2083          btree_get(btree, &vals[i]);
  2084      })
  2085  
  2086  
  2087      shuffle(vals, N, sizeof(int));
  2088      bench("delete (rand)", N, {
  2089          btree_delete(btree, &vals[i]);
  2090      })
  2091      shuffle(vals, N, sizeof(int));
  2092      for (int i = 0; i < N; i++) {
  2093          btree_set(btree, &vals[i]);
  2094      }
  2095  
  2096      bench("min", N, {
  2097          assert(btree_min(btree));
  2098      })
  2099  
  2100      bench("max", N, {
  2101          assert(btree_max(btree));
  2102      })
  2103  
  2104      bench("ascend", N, {
  2105          btree_ascend(btree, NULL, simple_iter, NULL);
  2106          break;
  2107      })
  2108  
  2109      bench("descend", N, {
  2110          btree_descend(btree, NULL, simple_iter, NULL);
  2111          break;
  2112      })
  2113  
  2114      bench("pop-min", N, {
  2115          btree_pop_min(btree);
  2116      })
  2117  
  2118      // -- pop last items from tree --
  2119      // reinsert
  2120      shuffle(vals, N, sizeof(int));
  2121      for (int i = 0; i < N; i++) {
  2122          btree_set(btree, &vals[i]);
  2123      }
  2124      bench("pop-max", N, {
  2125          btree_pop_max(btree);
  2126      })
  2127  
  2128      // -- delete all odd value items from the tree --
  2129      // reinsert
  2130      shuffle(vals, N, sizeof(int));
  2131      for (int i = 0; i < N; i++) {
  2132          btree_set(btree, &vals[i]);
  2133      }
  2134      qsort(vals, N, sizeof(int), compare_ints_nudata);
  2135      int count = 0;
  2136      bench("asc-del-odds", N, {
  2137          btree_action_ascend(btree, NULL, del_asc_odds, &count);
  2138          break;
  2139      });
  2140  
  2141      // reinsert
  2142      for (int i = 0; i < N; i++) {
  2143          btree_set(btree, &vals[i]);
  2144      }
  2145      count = 0;
  2146      bench("desc-del-odds", N, {
  2147          btree_action_descend(btree, NULL, del_asc_odds, &count);
  2148          break;
  2149      });
  2150  
  2151  
  2152  
  2153      btree_free(btree);
  2154      xfree(vals);
  2155  }
  2156  
  2157  
  2158  
  2159  int main() {
  2160      btree_set_allocator(xmalloc, xfree);
  2161  
  2162      if (getenv("BENCH")) {
  2163          printf("Running btree.c benchmarks...\n");
  2164          benchmarks();
  2165      } else {
  2166          printf("Running btree.c tests...\n");
  2167          test_basic();
  2168          test_action_ascend();
  2169          printf("PASSED\n");
  2170      }
  2171  }
  2172  
  2173  #endif