Raw
1 #include "git-compat-util.h"
2 #include "diff.h"
3 #include "diff-provider-internal.h"
4 #include "replace-object.h"
5 #include "repository.h"
6
7 /*
8 * The terminal provider: the builtin computation. A request that
9 * carries a fill callback is answered by loading the pair's content
10 * and running xdiff, so a walk that reaches it never falls through
11 * to the consumer. On a consult-only walk it passes, and the walk's
12 * fall-through outcome tells the consumer to compute.
13 */
14 static enum diff_provider_disposition
15 builtin_consult(struct diff_provider *provider UNUSED,
16 const struct diff_provider_request *req,
17 diff_provider_fill_fn fill, void *fill_data,
18 xdl_emit_hunk_consume_func_t hunk_cb, void *cb_data)
19 {
20 xdemitconf_t xecfg = { .hunk_func = hunk_cb };
21 xdemitcb_t ecb = { .priv = cb_data };
22 mmfile_t old_file, new_file;
23
24 if (!fill)
25 return DIFF_PROVIDER_DISP_PASS;
26 if (fill(fill_data, &old_file, &new_file) < 0)
27 return DIFF_PROVIDER_DISP_ERROR;
28 if (xdi_diff(&old_file, &new_file, req->xpp, &xecfg, &ecb) < 0)
29 return DIFF_PROVIDER_DISP_ERROR;
30 return DIFF_PROVIDER_DISP_ANSWERED;
31 }
32
33 static struct diff_provider *builtin_provider_new(void)
34 {
35 struct diff_provider *p = xcalloc(1, sizeof(*p));
36
37 p->consult = builtin_consult;
38 p->computes = 1;
39 return p;
40 }
41
42 /*
43 * The repository's chain, assembled on first walk. The composition
44 * is fixed, and the order is the authority resolution: the process
45 * outranks the store, and the builtin computation is the terminal
46 * provider, so the chain always ends in an implementor that can
47 * answer. Nothing is decided per repository here; each provider
48 * gates itself per request.
49 */
50 static struct diff_provider *provider_chain(struct repository *r)
51 {
52 struct diff_provider **tail = &r->diff_providers;
53
54 if (*tail)
55 return *tail;
56 *tail = diff_process_provider_new();
57 tail = &(*tail)->next;
58 *tail = diff_hunks_store_provider_new();
59 tail = &(*tail)->next;
60 *tail = builtin_provider_new();
61 return r->diff_providers;
62 }
63
64 void diff_providers_clear(struct repository *r)
65 {
66 struct diff_provider *p = r->diff_providers;
67
68 while (p) {
69 struct diff_provider *next = p->next;
70
71 if (p->release)
72 p->release(p);
73 free(p);
74 p = next;
75 }
76 r->diff_providers = NULL;
77 }
78
79 /*
80 * The walk shared by diff_provider_consult() and
81 * diff_provider_emit_hunks(): consult the chain in order and map its
82 * dispositions onto the outcome set. The first answer ends the
83 * walk. A stop-no-record disposition (diff-provider-internal.h)
84 * is a refusal, not a pass: the provider does not answer, but rules
85 * the pair out of identity service and out of recording, so from
86 * then on the walk consults only the computing provider, and a walk
87 * that ends unanswered carries the no-record verdict. With a fill
88 * callback the terminal provider computes instead of passing, so an
89 * emit walk returns only answered or error.
90 */
91 static enum diff_provider_outcome
92 walk_providers(const struct diff_provider_request *req,
93 diff_provider_fill_fn fill, void *fill_data,
94 xdl_emit_hunk_consume_func_t hunk_cb, void *cb_data)
95 {
96 struct diff_provider *p;
97 int no_record = 0;
98
99 if (req->diffopt && req->diffopt->repo != req->repo)
100 BUG("diff provider request walks one repository's chain "
101 "with another repository's diff options");
102
103 /*
104 * An object replacement redirects a blob's content
105 * (OBJECT_INFO_LOOKUP_REPLACE) while leaving the id that names it
106 * unchanged, so an answer keyed on the raw id would be the
107 * pre-replacement diff. A replacement is therefore a parameter
108 * outside the recording key: no provider may serve a replaced pair
109 * from its identity, and a result computed for it must not be
110 * recorded under the raw id. Mark the walk no-record so the
111 * identity providers step aside and the builtin computes from the
112 * replaced content. The check is a no-op when the repository has
113 * no replace refs.
114 */
115 if ((req->old_oid &&
116 lookup_replace_object(req->repo, req->old_oid) != req->old_oid) ||
117 (req->new_oid &&
118 lookup_replace_object(req->repo, req->new_oid) != req->new_oid))
119 no_record = 1;
120
121 for (p = provider_chain(req->repo); p; p = p->next) {
122 enum diff_provider_disposition disp;
123
124 if (no_record && !p->computes)
125 continue;
126 disp = p->consult(p, req, fill, fill_data,
127 hunk_cb, cb_data);
128 if (disp == DIFF_PROVIDER_DISP_ERROR && !p->computes)
129 BUG("only the computing provider may return the "
130 "error disposition");
131 if (p->computes && !fill && disp != DIFF_PROVIDER_DISP_PASS)
132 BUG("the computing provider must pass on a "
133 "fill-less walk");
134 switch (disp) {
135 case DIFF_PROVIDER_DISP_ANSWERED:
136 return DIFF_PROVIDER_ANSWERED;
137 case DIFF_PROVIDER_DISP_PASS:
138 continue;
139 case DIFF_PROVIDER_DISP_STOP_NO_RECORD:
140 no_record = 1;
141 continue;
142 case DIFF_PROVIDER_DISP_ERROR:
143 return DIFF_PROVIDER_ERROR;
144 }
145 }
146 return no_record ? DIFF_PROVIDER_UNANSWERED_NO_RECORD :
147 DIFF_PROVIDER_UNANSWERED;
148 }
149
150 enum diff_provider_outcome
151 diff_provider_consult(const struct diff_provider_request *req,
152 xdl_emit_hunk_consume_func_t hunk_cb, void *cb_data)
153 {
154 return walk_providers(req, NULL, NULL, hunk_cb, cb_data);
155 }
156
157 enum diff_provider_hunks_error
158 diff_provider_check_hunk(struct diff_provider_hunks_check *c,
159 long old_start, long old_count,
160 long new_start, long new_count)
161 {
162 if (old_start < 0 || old_count < 0 ||
163 new_start < 0 || new_count < 0 ||
164 old_start > INT32_MAX || old_count > INT32_MAX ||
165 new_start > INT32_MAX || new_count > INT32_MAX ||
166 (int64_t)old_start + old_count > INT32_MAX ||
167 (int64_t)new_start + new_count > INT32_MAX)
168 return DIFF_PROVIDER_HUNKS_RANGE;
169 if (old_start < c->prev_old_end || new_start < c->prev_new_end)
170 return DIFF_PROVIDER_HUNKS_OVERLAP;
171 if (old_start - c->prev_old_end != new_start - c->prev_new_end)
172 return DIFF_PROVIDER_HUNKS_MISALIGNED;
173 /*
174 * With each field bounded to int32 above, the int64 sums cannot
175 * overflow even where long is 32-bit, and the range rule has
176 * already capped them at INT32_MAX.
177 */
178 c->prev_old_end = (int64_t)old_start + old_count;
179 c->prev_new_end = (int64_t)new_start + new_count;
180 return DIFF_PROVIDER_HUNKS_OK;
181 }
182
183 enum diff_provider_outcome
184 diff_provider_emit_hunks(const struct diff_provider_request *req,
185 diff_provider_fill_fn fill, void *fill_data,
186 xdl_emit_hunk_consume_func_t hunk_cb,
187 void *cb_data)
188 {
189 return walk_providers(req, fill, fill_data, hunk_cb, cb_data);
190 }