oidtree: add ability to store data
The oidtree data structure is currently only used to store object IDs, without any associated data. So consequently, it can only really be used to track which object IDs exist, and we can use the tree structure to efficiently operate on OID prefixes. But there are valid use cases where we want to both: - Store object IDs in a sorted order. - Associated arbitrary data with them. Refactor the oidtree interface so that it allows us to store arbitrary payloads within the respective nodes. This will be used in the next commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Patrick Steinhardt committed
Apr 10, 2026 at 14:12 UTC
449650decf49b1fe5b1dac1c48dfb919e9b57b0d
5 files changed
+68
-12
loose.c
+1
-1
@@ -57,7 +57,7 @@ static int insert_loose_map(struct odb_source *source,
57
inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
58
inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
59
if (inserted)
60
- oidtree_insert(files->loose->cache, compat_oid);
60
+ oidtree_insert(files->loose->cache, compat_oid, NULL);
61
62
return inserted;
63
}
object-file.c
+2
-1
@@ -1858,6 +1858,7 @@ static int for_each_object_wrapper_cb(const struct object_id *oid,
1858
}
1859
1860
static int for_each_prefixed_object_wrapper_cb(const struct object_id *oid,
1861
+ void *node_data UNUSED,
1862
void *cb_data)
1863
{
1864
struct for_each_object_wrapper_data *data = cb_data;
@@ -2003,7 +2004,7 @@ static int append_loose_object(const struct object_id *oid,
2004
const char *path UNUSED,
2005
void *data)
2006
{
2006
- oidtree_insert(data, oid);
2007
+ oidtree_insert(data, oid, NULL);
2008
return 0;
2009
}
2010
oidtree.c
+32
-5
@@ -9,6 +9,7 @@
9
struct oidtree_node {
10
struct cb_node base;
11
struct object_id key;
12
+ void *data;
13
};
14
15
void oidtree_init(struct oidtree *ot)
@@ -25,15 +26,22 @@ void oidtree_clear(struct oidtree *ot)
26
}
27
}
28
28
-void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
29
+struct oidtree_data {
30
+ struct object_id oid;
31
+};
32
+
33
+void oidtree_insert(struct oidtree *ot, const struct object_id *oid,
34
+ void *data)
35
{
36
struct oidtree_node *on;
37
+ struct cb_node *node;
38
39
if (!oid->algo)
40
BUG("oidtree_insert requires oid->algo");
41
42
on = mem_pool_alloc(&ot->mem_pool, sizeof(*on));
43
oidcpy(&on->key, oid);
44
+ on->data = data;
45
46
/*
47
* n.b. Current callers won't get us duplicates, here. If a
@@ -41,13 +49,19 @@ void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
49
* that won't be freed until oidtree_clear. Currently it's not
50
* worth maintaining a free list
51
*/
44
- cb_insert(&ot->tree, &on->base, sizeof(*oid));
52
+ node = cb_insert(&ot->tree, &on->base, sizeof(*oid));
53
+ if (node) {
54
+ struct oidtree_node *preexisting = container_of(node, struct oidtree_node, base);
55
+ preexisting->data = data;
56
+ }
57
}
58
47
-bool oidtree_contains(struct oidtree *ot, const struct object_id *oid)
59
+static struct oidtree_node *oidtree_lookup(struct oidtree *ot,
60
+ const struct object_id *oid)
61
{
62
struct object_id k;
63
size_t klen = sizeof(k);
64
+ struct cb_node *node;
65
66
oidcpy(&k, oid);
67
@@ -58,7 +72,20 @@ bool oidtree_contains(struct oidtree *ot, const struct object_id *oid)
72
klen += BUILD_ASSERT_OR_ZERO(offsetof(struct object_id, hash) <
73
offsetof(struct object_id, algo));
74
61
- return !!cb_lookup(&ot->tree, (const uint8_t *)&k, klen);
75
+ node = cb_lookup(&ot->tree, (const uint8_t *)&k, klen);
76
+ return node ? container_of(node, struct oidtree_node, base) : NULL;
77
+}
78
+
79
+bool oidtree_contains(struct oidtree *ot, const struct object_id *oid)
80
+{
81
+ struct oidtree_node *node = oidtree_lookup(ot, oid);
82
+ return node ? 1 : 0;
83
+}
84
+
85
+void *oidtree_get(struct oidtree *ot, const struct object_id *oid)
86
+{
87
+ struct oidtree_node *node = oidtree_lookup(ot, oid);
88
+ return node ? node->data : NULL;
89
}
90
91
struct oidtree_each_data {
@@ -82,7 +109,7 @@ static int iter(struct cb_node *n, void *cb_data)
109
return 0;
110
}
111
85
- return data->cb(&node->key, data->cb_data);
112
+ return data->cb(&node->key, node->data, data->cb_data);
113
}
114
115
int oidtree_each(struct oidtree *ot, const struct object_id *prefix,
oidtree.h
+10
-2
@@ -29,18 +29,26 @@ void oidtree_init(struct oidtree *ot);
29
*/
30
void oidtree_clear(struct oidtree *ot);
31
32
-/* Insert the object ID into the tree. */
33
-void oidtree_insert(struct oidtree *ot, const struct object_id *oid);
32
+/*
33
+ * Insert the object ID into the tree and store the given pointer alongside
34
+ * with it. The data pointer of any preexisting entry will be overwritten.
35
+ */
36
+void oidtree_insert(struct oidtree *ot, const struct object_id *oid,
37
+ void *data);
38
39
/* Check whether the tree contains the given object ID. */
40
bool oidtree_contains(struct oidtree *ot, const struct object_id *oid);
41
42
+/* Get the payload stored with the given object ID. */
43
+void *oidtree_get(struct oidtree *ot, const struct object_id *oid);
44
+
45
/*
46
* Callback function used for `oidtree_each()`. Returning a non-zero exit code
47
* will cause iteration to stop. The exit code will be propagated to the caller
48
* of `oidtree_each()`.
49
*/
50
typedef int (*oidtree_each_cb)(const struct object_id *oid,
51
+ void *node_data,
52
void *cb_data);
53
54
/*
t/unit-tests/u-oidtree.c
+23
-3
@@ -19,7 +19,7 @@ static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
19
for (size_t i = 0; i < n; i++) {
20
struct object_id oid;
21
cl_parse_any_oid(hexes[i], &oid);
22
- oidtree_insert(ot, &oid);
22
+ oidtree_insert(ot, &oid, NULL);
23
}
24
return 0;
25
}
@@ -38,9 +38,9 @@ struct expected_hex_iter {
38
const char *query;
39
};
40
41
-static int check_each_cb(const struct object_id *oid, void *data)
41
+static int check_each_cb(const struct object_id *oid, void *node_data UNUSED, void *cb_data)
42
{
43
- struct expected_hex_iter *hex_iter = data;
43
+ struct expected_hex_iter *hex_iter = cb_data;
44
struct object_id expected;
45
46
cl_assert(hex_iter->i < hex_iter->expected_hexes.nr);
@@ -105,3 +105,23 @@ void test_oidtree__each(void)
105
check_each(&ot, "32100", "321", NULL);
106
check_each(&ot, "32", "320", "321", NULL);
107
}
108
+
109
+void test_oidtree__insert_overwrites_data(void)
110
+{
111
+ struct object_id oid;
112
+ struct oidtree ot;
113
+ int a, b;
114
+
115
+ cl_parse_any_oid("1", &oid);
116
+
117
+ oidtree_init(&ot);
118
+
119
+ oidtree_insert(&ot, &oid, NULL);
120
+ cl_assert_equal_p(oidtree_get(&ot, &oid), NULL);
121
+ oidtree_insert(&ot, &oid, &a);
122
+ cl_assert_equal_p(oidtree_get(&ot, &oid), &a);
123
+ oidtree_insert(&ot, &oid, &b);
124
+ cl_assert_equal_p(oidtree_get(&ot, &oid), &b);
125
+
126
+ oidtree_clear(&ot);
127
+}