Browse Source
The reftable format includes support for an (OID => ref) map. This map can speed up visibility and reachability checks. In particular, various operations along the fetch/push path within Gerrit have ben sped up by using this structure. The map is constructed with help of a binary tree. Object IDs are hashes, so they are uniformly distributed. Hence, the tree does not attempt forced rebalancing. Signed-off-by: Han-Wen Nienhuys <hanwen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint


5 changed files with 162 additions and 1 deletions
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/* |
||||
Copyright 2020 Google LLC |
||||
|
||||
Use of this source code is governed by a BSD-style |
||||
license that can be found in the LICENSE file or at |
||||
https://developers.google.com/open-source/licenses/bsd |
||||
*/ |
||||
|
||||
#include "tree.h" |
||||
|
||||
#include "basics.h" |
||||
#include "system.h" |
||||
|
||||
struct tree_node *tree_search(void *key, struct tree_node **rootp, |
||||
int (*compare)(const void *, const void *), |
||||
int insert) |
||||
{ |
||||
int res; |
||||
if (*rootp == NULL) { |
||||
if (!insert) { |
||||
return NULL; |
||||
} else { |
||||
struct tree_node *n = |
||||
reftable_calloc(sizeof(struct tree_node)); |
||||
n->key = key; |
||||
*rootp = n; |
||||
return *rootp; |
||||
} |
||||
} |
||||
|
||||
res = compare(key, (*rootp)->key); |
||||
if (res < 0) |
||||
return tree_search(key, &(*rootp)->left, compare, insert); |
||||
else if (res > 0) |
||||
return tree_search(key, &(*rootp)->right, compare, insert); |
||||
return *rootp; |
||||
} |
||||
|
||||
void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key), |
||||
void *arg) |
||||
{ |
||||
if (t->left) { |
||||
infix_walk(t->left, action, arg); |
||||
} |
||||
action(arg, t->key); |
||||
if (t->right) { |
||||
infix_walk(t->right, action, arg); |
||||
} |
||||
} |
||||
|
||||
void tree_free(struct tree_node *t) |
||||
{ |
||||
if (t == NULL) { |
||||
return; |
||||
} |
||||
if (t->left) { |
||||
tree_free(t->left); |
||||
} |
||||
if (t->right) { |
||||
tree_free(t->right); |
||||
} |
||||
reftable_free(t); |
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
/* |
||||
Copyright 2020 Google LLC |
||||
|
||||
Use of this source code is governed by a BSD-style |
||||
license that can be found in the LICENSE file or at |
||||
https://developers.google.com/open-source/licenses/bsd |
||||
*/ |
||||
|
||||
#ifndef TREE_H |
||||
#define TREE_H |
||||
|
||||
/* tree_node is a generic binary search tree. */ |
||||
struct tree_node { |
||||
void *key; |
||||
struct tree_node *left, *right; |
||||
}; |
||||
|
||||
/* looks for `key` in `rootp` using `compare` as comparison function. If insert |
||||
* is set, insert the key if it's not found. Else, return NULL. |
||||
*/ |
||||
struct tree_node *tree_search(void *key, struct tree_node **rootp, |
||||
int (*compare)(const void *, const void *), |
||||
int insert); |
||||
|
||||
/* performs an infix walk of the tree. */ |
||||
void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key), |
||||
void *arg); |
||||
|
||||
/* |
||||
* deallocates the tree nodes recursively. Keys should be deallocated separately |
||||
* by walking over the tree. */ |
||||
void tree_free(struct tree_node *t); |
||||
|
||||
#endif |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/* |
||||
Copyright 2020 Google LLC |
||||
|
||||
Use of this source code is governed by a BSD-style |
||||
license that can be found in the LICENSE file or at |
||||
https://developers.google.com/open-source/licenses/bsd |
||||
*/ |
||||
|
||||
#include "tree.h" |
||||
|
||||
#include "basics.h" |
||||
#include "record.h" |
||||
#include "test_framework.h" |
||||
#include "reftable-tests.h" |
||||
|
||||
static int test_compare(const void *a, const void *b) |
||||
{ |
||||
return (char *)a - (char *)b; |
||||
} |
||||
|
||||
struct curry { |
||||
void *last; |
||||
}; |
||||
|
||||
static void check_increasing(void *arg, void *key) |
||||
{ |
||||
struct curry *c = arg; |
||||
if (c->last) { |
||||
EXPECT(test_compare(c->last, key) < 0); |
||||
} |
||||
c->last = key; |
||||
} |
||||
|
||||
static void test_tree(void) |
||||
{ |
||||
struct tree_node *root = NULL; |
||||
|
||||
void *values[11] = { NULL }; |
||||
struct tree_node *nodes[11] = { NULL }; |
||||
int i = 1; |
||||
struct curry c = { NULL }; |
||||
do { |
||||
nodes[i] = tree_search(values + i, &root, &test_compare, 1); |
||||
i = (i * 7) % 11; |
||||
} while (i != 1); |
||||
|
||||
for (i = 1; i < ARRAY_SIZE(nodes); i++) { |
||||
EXPECT(values + i == nodes[i]->key); |
||||
EXPECT(nodes[i] == |
||||
tree_search(values + i, &root, &test_compare, 0)); |
||||
} |
||||
|
||||
infix_walk(root, check_increasing, &c); |
||||
tree_free(root); |
||||
} |
||||
|
||||
int tree_test_main(int argc, const char *argv[]) |
||||
{ |
||||
RUN_TEST(test_tree); |
||||
return 0; |
||||
} |
Loading…
Reference in new issue