reftable: a generic binary tree implementation

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>

Han-Wen Nienhuys committed Oct 7, 2021 at 20:25 UTC 35425d1034f2a98d438da21c68334056af7033ca
5 files changed +162 -1
Makefile
+3 -1
@@ -2462,11 +2462,13 @@ REFTABLE_OBJS += reftable/block.o
2462 REFTABLE_OBJS += reftable/blocksource.o
2463 REFTABLE_OBJS += reftable/publicbasics.o
2464 REFTABLE_OBJS += reftable/record.o
2465 +REFTABLE_OBJS += reftable/tree.o
2466
2467 +REFTABLE_TEST_OBJS += reftable/basics_test.o
2468 REFTABLE_TEST_OBJS += reftable/block_test.o
2469 REFTABLE_TEST_OBJS += reftable/record_test.o
2470 REFTABLE_TEST_OBJS += reftable/test_framework.o
2469 -REFTABLE_TEST_OBJS += reftable/basics_test.o
2471 +REFTABLE_TEST_OBJS += reftable/tree_test.o
2472
2473 TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
2474
reftable/tree.c new
+63
@@ -0,0 +1,63 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#include "tree.h"
10 +
11 +#include "basics.h"
12 +#include "system.h"
13 +
14 +struct tree_node *tree_search(void *key, struct tree_node **rootp,
15 + int (*compare)(const void *, const void *),
16 + int insert)
17 +{
18 + int res;
19 + if (*rootp == NULL) {
20 + if (!insert) {
21 + return NULL;
22 + } else {
23 + struct tree_node *n =
24 + reftable_calloc(sizeof(struct tree_node));
25 + n->key = key;
26 + *rootp = n;
27 + return *rootp;
28 + }
29 + }
30 +
31 + res = compare(key, (*rootp)->key);
32 + if (res < 0)
33 + return tree_search(key, &(*rootp)->left, compare, insert);
34 + else if (res > 0)
35 + return tree_search(key, &(*rootp)->right, compare, insert);
36 + return *rootp;
37 +}
38 +
39 +void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key),
40 + void *arg)
41 +{
42 + if (t->left) {
43 + infix_walk(t->left, action, arg);
44 + }
45 + action(arg, t->key);
46 + if (t->right) {
47 + infix_walk(t->right, action, arg);
48 + }
49 +}
50 +
51 +void tree_free(struct tree_node *t)
52 +{
53 + if (t == NULL) {
54 + return;
55 + }
56 + if (t->left) {
57 + tree_free(t->left);
58 + }
59 + if (t->right) {
60 + tree_free(t->right);
61 + }
62 + reftable_free(t);
63 +}
reftable/tree.h new
+34
@@ -0,0 +1,34 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#ifndef TREE_H
10 +#define TREE_H
11 +
12 +/* tree_node is a generic binary search tree. */
13 +struct tree_node {
14 + void *key;
15 + struct tree_node *left, *right;
16 +};
17 +
18 +/* looks for `key` in `rootp` using `compare` as comparison function. If insert
19 + * is set, insert the key if it's not found. Else, return NULL.
20 + */
21 +struct tree_node *tree_search(void *key, struct tree_node **rootp,
22 + int (*compare)(const void *, const void *),
23 + int insert);
24 +
25 +/* performs an infix walk of the tree. */
26 +void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key),
27 + void *arg);
28 +
29 +/*
30 + * deallocates the tree nodes recursively. Keys should be deallocated separately
31 + * by walking over the tree. */
32 +void tree_free(struct tree_node *t);
33 +
34 +#endif
reftable/tree_test.c new
+61
@@ -0,0 +1,61 @@
1 +/*
2 +Copyright 2020 Google LLC
3 +
4 +Use of this source code is governed by a BSD-style
5 +license that can be found in the LICENSE file or at
6 +https://developers.google.com/open-source/licenses/bsd
7 +*/
8 +
9 +#include "tree.h"
10 +
11 +#include "basics.h"
12 +#include "record.h"
13 +#include "test_framework.h"
14 +#include "reftable-tests.h"
15 +
16 +static int test_compare(const void *a, const void *b)
17 +{
18 + return (char *)a - (char *)b;
19 +}
20 +
21 +struct curry {
22 + void *last;
23 +};
24 +
25 +static void check_increasing(void *arg, void *key)
26 +{
27 + struct curry *c = arg;
28 + if (c->last) {
29 + EXPECT(test_compare(c->last, key) < 0);
30 + }
31 + c->last = key;
32 +}
33 +
34 +static void test_tree(void)
35 +{
36 + struct tree_node *root = NULL;
37 +
38 + void *values[11] = { NULL };
39 + struct tree_node *nodes[11] = { NULL };
40 + int i = 1;
41 + struct curry c = { NULL };
42 + do {
43 + nodes[i] = tree_search(values + i, &root, &test_compare, 1);
44 + i = (i * 7) % 11;
45 + } while (i != 1);
46 +
47 + for (i = 1; i < ARRAY_SIZE(nodes); i++) {
48 + EXPECT(values + i == nodes[i]->key);
49 + EXPECT(nodes[i] ==
50 + tree_search(values + i, &root, &test_compare, 0));
51 + }
52 +
53 + infix_walk(root, check_increasing, &c);
54 + tree_free(root);
55 +}
56 +
57 +int tree_test_main(int argc, const char *argv[])
58 +{
59 + RUN_TEST(test_tree);
60 + return 0;
61 +}
t/helper/test-reftable.c
+1
@@ -6,5 +6,6 @@ int cmd__reftable(int argc, const char **argv)
6 basics_test_main(argc, argv);
7 block_test_main(argc, argv);
8 record_test_main(argc, argv);
9 + tree_test_main(argc, argv);
10 return 0;
11 }