-
Notifications
You must be signed in to change notification settings - Fork 59
/
cinnabar-notes.c
220 lines (191 loc) · 6.06 KB
/
cinnabar-notes.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#include "cache.h"
#include "cinnabar-notes.h"
#undef notes_tree
#undef init_notes
#undef free_notes
#undef add_note
#undef remove_note
#undef get_note
#undef for_each_note
#undef write_notes_tree
static int abbrev_sha1_cmp(const unsigned char *ref_sha1,
const unsigned char *abbrev_sha1, size_t len)
{
int i;
for (i = 0; i < len / 2; i++, ref_sha1++, abbrev_sha1++) {
if (*ref_sha1 != *abbrev_sha1)
return *ref_sha1 - *abbrev_sha1;
}
if (len % 2) {
unsigned char ref_bits = *ref_sha1 & 0xf0;
unsigned char abbrev_bits = *abbrev_sha1 & 0xf0;
if (ref_bits != abbrev_bits)
return ref_bits - abbrev_bits;
}
return 0;
}
/* Definitions from git's notes.c. See there for more details */
struct int_node {
void *a[16];
};
struct leaf_node {
struct object_id key_oid;
struct object_id val_oid;
};
#define PTR_TYPE_NULL 0
#define PTR_TYPE_INTERNAL 1
#define PTR_TYPE_NOTE 2
#define PTR_TYPE_SUBTREE 3
#define GET_PTR_TYPE(ptr) ((uintptr_t) (ptr) & 3)
#define CLR_PTR_TYPE(ptr) ((void *) ((uintptr_t) (ptr) & ~3))
#define GET_NIBBLE(n, sha1) (((sha1[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
/* This function assumes the note tree has been populated for the given key,
* which means get_note must have been called before */
static struct leaf_node *note_tree_abbrev_find(struct notes_tree *t,
struct int_node *tree, unsigned char n,
const unsigned char *key_sha1, size_t len)
{
unsigned char i, j;
void *p;
if (n >= len) {
for (i = 17, j = 0; j < 16; j++) {
if (tree->a[j])
i = (i < 17) ? 16 : j;
}
if (i >= 16)
return NULL;
} else {
i = GET_NIBBLE(n, key_sha1);
}
p = tree->a[i];
switch (GET_PTR_TYPE(p)) {
case PTR_TYPE_INTERNAL:
tree = CLR_PTR_TYPE(p);
return note_tree_abbrev_find(t, tree, ++n, key_sha1, len);
case PTR_TYPE_SUBTREE:
return NULL;
default:
{
struct leaf_node *node = CLR_PTR_TYPE(p);
if (node && !abbrev_sha1_cmp(node->key_oid.hash,
key_sha1, len))
return node;
return NULL;
}
}
}
const struct object_id *get_abbrev_note(struct cinnabar_notes_tree *t,
const struct object_id *object_oid, size_t len)
{
struct leaf_node *found;
assert(t);
assert(notes_initialized(t));
found = note_tree_abbrev_find(&t->current, t->current.root, 0,
object_oid->hash, len);
if (!found)
found = note_tree_abbrev_find(
&t->additions, t->additions.root, 0,
object_oid->hash, len);
return found ? &found->val_oid : NULL;
}
void cinnabar_init_notes(struct cinnabar_notes_tree *t, const char *notes_ref,
combine_notes_fn combine_notes, int flags)
{
t->init_flags = flags;
init_notes(&t->current, notes_ref, combine_notes, flags);
init_notes(&t->additions, notes_ref, combine_notes_ignore,
NOTES_INIT_EMPTY);
}
void cinnabar_free_notes(struct cinnabar_notes_tree *t)
{
free_notes(&t->current);
free_notes(&t->additions);
}
int cinnabar_add_note(
struct cinnabar_notes_tree *t, const struct object_id *object_oid,
const struct object_id *note_oid, combine_notes_fn combine_notes)
{
if (!combine_notes)
combine_notes = t->current.combine_notes;
if (combine_notes == combine_notes_ignore) {
if (get_note(&t->current, object_oid))
return 0;
} else if (combine_notes != combine_notes_overwrite) {
die("Unsupported combine_notes");
}
return add_note(&t->additions, object_oid, note_oid, NULL);
}
int cinnabar_remove_note(struct cinnabar_notes_tree *t,
const unsigned char *object_sha1)
{
int result = remove_note(&t->current, object_sha1);
int result2 = remove_note(&t->additions, object_sha1);
if (!result) {
struct object_id oid;
hashcpy(oid.hash, object_sha1);
oid.algo = GIT_HASH_SHA1;
add_note(&t->additions, &oid, null_oid(), NULL);
}
return result && result2;
}
const struct object_id *cinnabar_get_note(struct cinnabar_notes_tree *t,
const struct object_id *object_oid)
{
const struct object_id *note = get_note(&t->current, object_oid);
if (!note) {
note = get_note(&t->additions, object_oid);
if (note && is_null_oid(note))
note = NULL;
}
return note;
}
static int merge_note(const struct object_id *object_oid,
const struct object_id *note_oid, char *note_path,
void *data)
{
struct notes_tree *notes = (struct notes_tree *)data;
if (is_null_oid(note_oid))
remove_note(notes, object_oid->hash);
else
add_note(notes, object_oid, note_oid, combine_notes_overwrite);
return 0;
}
int cinnabar_for_each_note(struct cinnabar_notes_tree *t, int flags,
each_note_fn fn, void *cb_data)
{
consolidate_notes(t);
return for_each_note(&t->current, flags, fn, cb_data);
}
extern int write_notes_tree_mode(
struct notes_tree *t, struct object_id *result, unsigned int mode);
int cinnabar_write_notes_tree(struct cinnabar_notes_tree *t,
struct object_id *result, unsigned int mode)
{
consolidate_notes(t);
return write_notes_tree_mode(&t->current, result, mode);
}
void consolidate_notes(struct cinnabar_notes_tree *t) {
struct int_node empty_node = { { NULL, } };
char *notes_ref = xstrdup_or_null(t->current.ref);
if (memcmp(t->current.root, &empty_node, sizeof(empty_node)) == 0) {
// If current is empty, we just copy the additions to it.
free_notes(&t->current);
memcpy(&t->current, &t->additions, sizeof(struct notes_tree));
t->additions.initialized = 0;
init_notes(&t->additions, notes_ref, combine_notes_ignore,
NOTES_INIT_EMPTY);
} else {
/* Reinitialize current */
combine_notes_fn combine_notes = t->current.combine_notes;
free_notes(&t->current);
init_notes(&t->current, notes_ref, combine_notes, t->init_flags);
/* Merge additions */
for_each_note(&t->additions, FOR_EACH_NOTE_DONT_UNPACK_SUBTREES,
merge_note, &t->current);
/* Reinitialize additions */
free_notes(&t->additions);
init_notes(&t->additions, notes_ref, combine_notes_ignore,
NOTES_INIT_EMPTY);
}
free(notes_ref);
}