]> git.ipfire.org Git - thirdparty/git.git/blob - reftable/refname.c
reftable/refname: refactor binary search over refnames
[thirdparty/git.git] / reftable / refname.c
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 "system.h"
10 #include "reftable-error.h"
11 #include "basics.h"
12 #include "refname.h"
13 #include "reftable-iterator.h"
14
15 struct refname_needle_lesseq_args {
16 char **haystack;
17 const char *needle;
18 };
19
20 static int refname_needle_lesseq(size_t k, void *_args)
21 {
22 struct refname_needle_lesseq_args *args = _args;
23 return strcmp(args->needle, args->haystack[k]) <= 0;
24 }
25
26 static int modification_has_ref(struct modification *mod, const char *name)
27 {
28 struct reftable_ref_record ref = { NULL };
29 int err = 0;
30
31 if (mod->add_len > 0) {
32 struct refname_needle_lesseq_args args = {
33 .haystack = mod->add,
34 .needle = name,
35 };
36 size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
37 if (idx < mod->add_len && !strcmp(mod->add[idx], name))
38 return 0;
39 }
40
41 if (mod->del_len > 0) {
42 struct refname_needle_lesseq_args args = {
43 .haystack = mod->del,
44 .needle = name,
45 };
46 size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
47 if (idx < mod->del_len && !strcmp(mod->del[idx], name))
48 return 1;
49 }
50
51 err = reftable_table_read_ref(&mod->tab, name, &ref);
52 reftable_ref_record_release(&ref);
53 return err;
54 }
55
56 static void modification_release(struct modification *mod)
57 {
58 /* don't delete the strings themselves; they're owned by ref records.
59 */
60 FREE_AND_NULL(mod->add);
61 FREE_AND_NULL(mod->del);
62 mod->add_len = 0;
63 mod->del_len = 0;
64 }
65
66 static int modification_has_ref_with_prefix(struct modification *mod,
67 const char *prefix)
68 {
69 struct reftable_iterator it = { NULL };
70 struct reftable_ref_record ref = { NULL };
71 int err = 0;
72
73 if (mod->add_len > 0) {
74 struct refname_needle_lesseq_args args = {
75 .haystack = mod->add,
76 .needle = prefix,
77 };
78 size_t idx = binsearch(mod->add_len, refname_needle_lesseq, &args);
79 if (idx < mod->add_len &&
80 !strncmp(prefix, mod->add[idx], strlen(prefix)))
81 goto done;
82 }
83 err = reftable_table_seek_ref(&mod->tab, &it, prefix);
84 if (err)
85 goto done;
86
87 while (1) {
88 err = reftable_iterator_next_ref(&it, &ref);
89 if (err)
90 goto done;
91
92 if (mod->del_len > 0) {
93 struct refname_needle_lesseq_args args = {
94 .haystack = mod->del,
95 .needle = ref.refname,
96 };
97 size_t idx = binsearch(mod->del_len, refname_needle_lesseq, &args);
98 if (idx < mod->del_len &&
99 !strcmp(ref.refname, mod->del[idx]))
100 continue;
101 }
102
103 if (strncmp(ref.refname, prefix, strlen(prefix))) {
104 err = 1;
105 goto done;
106 }
107 err = 0;
108 goto done;
109 }
110
111 done:
112 reftable_ref_record_release(&ref);
113 reftable_iterator_destroy(&it);
114 return err;
115 }
116
117 static int validate_refname(const char *name)
118 {
119 while (1) {
120 char *next = strchr(name, '/');
121 if (!*name) {
122 return REFTABLE_REFNAME_ERROR;
123 }
124 if (!next) {
125 return 0;
126 }
127 if (next - name == 0 || (next - name == 1 && *name == '.') ||
128 (next - name == 2 && name[0] == '.' && name[1] == '.'))
129 return REFTABLE_REFNAME_ERROR;
130 name = next + 1;
131 }
132 return 0;
133 }
134
135 int validate_ref_record_addition(struct reftable_table tab,
136 struct reftable_ref_record *recs, size_t sz)
137 {
138 struct modification mod = {
139 .tab = tab,
140 .add = reftable_calloc(sz, sizeof(*mod.add)),
141 .del = reftable_calloc(sz, sizeof(*mod.del)),
142 };
143 int i = 0;
144 int err = 0;
145 for (; i < sz; i++) {
146 if (reftable_ref_record_is_deletion(&recs[i])) {
147 mod.del[mod.del_len++] = recs[i].refname;
148 } else {
149 mod.add[mod.add_len++] = recs[i].refname;
150 }
151 }
152
153 err = modification_validate(&mod);
154 modification_release(&mod);
155 return err;
156 }
157
158 static void strbuf_trim_component(struct strbuf *sl)
159 {
160 while (sl->len > 0) {
161 int is_slash = (sl->buf[sl->len - 1] == '/');
162 strbuf_setlen(sl, sl->len - 1);
163 if (is_slash)
164 break;
165 }
166 }
167
168 int modification_validate(struct modification *mod)
169 {
170 struct strbuf slashed = STRBUF_INIT;
171 int err = 0;
172 int i = 0;
173 for (; i < mod->add_len; i++) {
174 err = validate_refname(mod->add[i]);
175 if (err)
176 goto done;
177 strbuf_reset(&slashed);
178 strbuf_addstr(&slashed, mod->add[i]);
179 strbuf_addstr(&slashed, "/");
180
181 err = modification_has_ref_with_prefix(mod, slashed.buf);
182 if (err == 0) {
183 err = REFTABLE_NAME_CONFLICT;
184 goto done;
185 }
186 if (err < 0)
187 goto done;
188
189 strbuf_reset(&slashed);
190 strbuf_addstr(&slashed, mod->add[i]);
191 while (slashed.len) {
192 strbuf_trim_component(&slashed);
193 err = modification_has_ref(mod, slashed.buf);
194 if (err == 0) {
195 err = REFTABLE_NAME_CONFLICT;
196 goto done;
197 }
198 if (err < 0)
199 goto done;
200 }
201 }
202 err = 0;
203 done:
204 strbuf_release(&slashed);
205 return err;
206 }