File Coverage

deps/libgit2/src/oid.c
Criterion Covered Total %
statement 89 202 44.0
branch 33 110 30.0
condition n/a
subroutine n/a
pod n/a
total 122 312 39.1


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "oid.h"
9              
10             #include "git2/oid.h"
11             #include "repository.h"
12             #include "global.h"
13             #include
14             #include
15              
16             static char to_hex[] = "0123456789abcdef";
17              
18 19           static int oid_error_invalid(const char *msg)
19             {
20 19           git_error_set(GIT_ERROR_INVALID, "unable to parse OID - %s", msg);
21 19           return -1;
22             }
23              
24 1419           int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
25             {
26             size_t p;
27             int v;
28              
29 1419 50         assert(out && str);
    50          
30              
31 1419 50         if (!length)
32 0           return oid_error_invalid("too short");
33              
34 1419 50         if (length > GIT_OID_HEXSZ)
35 0           return oid_error_invalid("too long");
36              
37 1419           memset(out->id, 0, GIT_OID_RAWSZ);
38              
39 56811 100         for (p = 0; p < length; p++) {
40 55411           v = git__fromhex(str[p]);
41 55411 100         if (v < 0)
42 19           return oid_error_invalid("contains invalid characters");
43              
44 55392 100         out->id[p / 2] |= (unsigned char)(v << (p % 2 ? 0 : 4));
45             }
46              
47 1400           return 0;
48             }
49              
50 0           int git_oid_fromstrp(git_oid *out, const char *str)
51             {
52 0           return git_oid_fromstrn(out, str, strlen(str));
53             }
54              
55 1149           int git_oid_fromstr(git_oid *out, const char *str)
56             {
57 1149           return git_oid_fromstrn(out, str, GIT_OID_HEXSZ);
58             }
59              
60 83339           GIT_INLINE(char) *fmt_one(char *str, unsigned int val)
61             {
62 83339           *str++ = to_hex[val >> 4];
63 83339           *str++ = to_hex[val & 0xf];
64 83339           return str;
65             }
66              
67 2656           int git_oid_nfmt(char *str, size_t n, const git_oid *oid)
68             {
69             size_t i, max_i;
70              
71 2656 100         if (!oid) {
72 1           memset(str, 0, n);
73 1           return 0;
74             }
75 2655 100         if (n > GIT_OID_HEXSZ) {
76 106           memset(&str[GIT_OID_HEXSZ], 0, n - GIT_OID_HEXSZ);
77 106           n = GIT_OID_HEXSZ;
78             }
79              
80 2655           max_i = n / 2;
81              
82 54454 100         for (i = 0; i < max_i; i++)
83 51799           str = fmt_one(str, oid->id[i]);
84              
85 2655 100         if (n & 1)
86 51           *str++ = to_hex[oid->id[i] >> 4];
87              
88 2655           return 0;
89             }
90              
91 618           int git_oid_fmt(char *str, const git_oid *oid)
92             {
93 618           return git_oid_nfmt(str, GIT_OID_HEXSZ, oid);
94             }
95              
96 1577           int git_oid_pathfmt(char *str, const git_oid *oid)
97             {
98             size_t i;
99              
100 1577           str = fmt_one(str, oid->id[0]);
101 1577           *str++ = '/';
102 31540 100         for (i = 1; i < sizeof(oid->id); i++)
103 29963           str = fmt_one(str, oid->id[i]);
104              
105 1577           return 0;
106             }
107              
108 20           char *git_oid_tostr_s(const git_oid *oid)
109             {
110 20           char *str = GIT_GLOBAL->oid_fmt;
111 20           git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
112 20           return str;
113             }
114              
115 7           char *git_oid_allocfmt(const git_oid *oid)
116             {
117 7           char *str = git__malloc(GIT_OID_HEXSZ + 1);
118 7 50         if (!str)
119 0           return NULL;
120 7           git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
121 7           return str;
122             }
123              
124 1932           char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
125             {
126 1932 50         if (!out || n == 0)
    50          
127 0           return "";
128              
129 1932 50         if (n > GIT_OID_HEXSZ + 1)
130 0           n = GIT_OID_HEXSZ + 1;
131              
132 1932           git_oid_nfmt(out, n - 1, oid); /* allow room for terminating NUL */
133 1932           out[n - 1] = '\0';
134              
135 1932           return out;
136             }
137              
138 586           int git_oid__parse(
139             git_oid *oid, const char **buffer_out,
140             const char *buffer_end, const char *header)
141             {
142 586           const size_t sha_len = GIT_OID_HEXSZ;
143 586           const size_t header_len = strlen(header);
144              
145 586           const char *buffer = *buffer_out;
146              
147 586 50         if (buffer + (header_len + sha_len + 1) > buffer_end)
148 0           return -1;
149              
150 586 100         if (memcmp(buffer, header, header_len) != 0)
151 268           return -1;
152              
153 318 50         if (buffer[header_len + sha_len] != '\n')
154 0           return -1;
155              
156 318 50         if (git_oid_fromstr(oid, buffer + header_len) < 0)
157 0           return -1;
158              
159 318           *buffer_out = buffer + (header_len + sha_len + 1);
160              
161 318           return 0;
162             }
163              
164 112           void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid)
165             {
166             char hex_oid[GIT_OID_HEXSZ];
167              
168 112           git_oid_fmt(hex_oid, oid);
169 112           git_buf_puts(buf, header);
170 112           git_buf_put(buf, hex_oid, GIT_OID_HEXSZ);
171 112           git_buf_putc(buf, '\n');
172 112           }
173              
174 35           int git_oid_fromraw(git_oid *out, const unsigned char *raw)
175             {
176 35           memcpy(out->id, raw, sizeof(out->id));
177 35           return 0;
178             }
179              
180 5285           int git_oid_cpy(git_oid *out, const git_oid *src)
181             {
182 5285           memcpy(out->id, src->id, sizeof(out->id));
183 5285           return 0;
184             }
185              
186 1299           int git_oid_cmp(const git_oid *a, const git_oid *b)
187             {
188 1299           return git_oid__cmp(a, b);
189             }
190              
191 6396           int git_oid_equal(const git_oid *a, const git_oid *b)
192             {
193 6396           return (git_oid__cmp(a, b) == 0);
194             }
195              
196 0           int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
197             {
198 0           const unsigned char *a = oid_a->id;
199 0           const unsigned char *b = oid_b->id;
200              
201 0 0         if (len > GIT_OID_HEXSZ)
202 0           len = GIT_OID_HEXSZ;
203              
204 0 0         while (len > 1) {
205 0 0         if (*a != *b)
206 0           return 1;
207 0           a++;
208 0           b++;
209 0           len -= 2;
210             };
211              
212 0 0         if (len)
213 0 0         if ((*a ^ *b) & 0xf0)
214 0           return 1;
215              
216 0           return 0;
217             }
218              
219 0           int git_oid_strcmp(const git_oid *oid_a, const char *str)
220             {
221             const unsigned char *a;
222             unsigned char strval;
223             int hexval;
224              
225 0 0         for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
    0          
226 0 0         if ((hexval = git__fromhex(*str++)) < 0)
227 0           return -1;
228 0           strval = (unsigned char)(hexval << 4);
229 0 0         if (*str) {
230 0 0         if ((hexval = git__fromhex(*str++)) < 0)
231 0           return -1;
232 0           strval |= hexval;
233             }
234 0 0         if (*a != strval)
235 0           return (*a - strval);
236             }
237              
238 0           return 0;
239             }
240              
241 0           int git_oid_streq(const git_oid *oid_a, const char *str)
242             {
243 0 0         return git_oid_strcmp(oid_a, str) == 0 ? 0 : -1;
244             }
245              
246 3215           int git_oid_is_zero(const git_oid *oid_a)
247             {
248 3215           const unsigned char *a = oid_a->id;
249             unsigned int i;
250 24842 100         for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)
251 23761 100         if (*a != 0)
252 2134           return 0;
253 1081           return 1;
254             }
255              
256 0           int git_oid_iszero(const git_oid *oid_a)
257             {
258 0           return git_oid_is_zero(oid_a);
259             }
260              
261             typedef short node_index;
262              
263             typedef union {
264             const char *tail;
265             node_index children[16];
266             } trie_node;
267              
268             struct git_oid_shorten {
269             trie_node *nodes;
270             size_t node_count, size;
271             int min_length, full;
272             };
273              
274 0           static int resize_trie(git_oid_shorten *self, size_t new_size)
275             {
276 0           self->nodes = git__reallocarray(self->nodes, new_size, sizeof(trie_node));
277 0 0         GIT_ERROR_CHECK_ALLOC(self->nodes);
278              
279 0 0         if (new_size > self->size) {
280 0           memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));
281             }
282              
283 0           self->size = new_size;
284 0           return 0;
285             }
286              
287 0           static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)
288             {
289             trie_node *node, *leaf;
290             node_index idx_leaf;
291              
292 0 0         if (os->node_count >= os->size) {
293 0 0         if (resize_trie(os, os->size * 2) < 0)
294 0           return NULL;
295             }
296              
297 0           idx_leaf = (node_index)os->node_count++;
298              
299 0 0         if (os->node_count == SHRT_MAX) {
300 0           os->full = 1;
301 0           return NULL;
302             }
303              
304 0           node = &os->nodes[idx];
305 0           node->children[push_at] = -idx_leaf;
306              
307 0           leaf = &os->nodes[idx_leaf];
308 0           leaf->tail = oid;
309              
310 0           return node;
311             }
312              
313 0           git_oid_shorten *git_oid_shorten_new(size_t min_length)
314             {
315             git_oid_shorten *os;
316              
317 0 0         assert((size_t)((int)min_length) == min_length);
318              
319 0           os = git__calloc(1, sizeof(git_oid_shorten));
320 0 0         if (os == NULL)
321 0           return NULL;
322              
323 0 0         if (resize_trie(os, 16) < 0) {
324 0           git__free(os);
325 0           return NULL;
326             }
327              
328 0           os->node_count = 1;
329 0           os->min_length = (int)min_length;
330              
331 0           return os;
332             }
333              
334 0           void git_oid_shorten_free(git_oid_shorten *os)
335             {
336 0 0         if (os == NULL)
337 0           return;
338              
339 0           git__free(os->nodes);
340 0           git__free(os);
341             }
342              
343              
344             /*
345             * What wizardry is this?
346             *
347             * This is just a memory-optimized trie: basically a very fancy
348             * 16-ary tree, which is used to store the prefixes of the OID
349             * strings.
350             *
351             * Read more: http://en.wikipedia.org/wiki/Trie
352             *
353             * Magic that happens in this method:
354             *
355             * - Each node in the trie is an union, so it can work both as
356             * a normal node, or as a leaf.
357             *
358             * - Each normal node points to 16 children (one for each possible
359             * character in the oid). This is *not* stored in an array of
360             * pointers, because in a 64-bit arch this would be sucking
361             * 16*sizeof(void*) = 128 bytes of memory per node, which is
362             * insane. What we do is store Node Indexes, and use these indexes
363             * to look up each node in the om->index array. These indexes are
364             * signed shorts, so this limits the amount of unique OIDs that
365             * fit in the structure to about 20000 (assuming a more or less uniform
366             * distribution).
367             *
368             * - All the nodes in om->index array are stored contiguously in
369             * memory, and each of them is 32 bytes, so we fit 2x nodes per
370             * cache line. Convenient for speed.
371             *
372             * - To differentiate the leafs from the normal nodes, we store all
373             * the indexes towards a leaf as a negative index (indexes to normal
374             * nodes are positives). When we find that one of the children for
375             * a node has a negative value, that means it's going to be a leaf.
376             * This reduces the amount of indexes we have by two, but also reduces
377             * the size of each node by 1-4 bytes (the amount we would need to
378             * add a `is_leaf` field): this is good because it allows the nodes
379             * to fit cleanly in cache lines.
380             *
381             * - Once we reach an empty children, instead of continuing to insert
382             * new nodes for each remaining character of the OID, we store a pointer
383             * to the tail in the leaf; if the leaf is reached again, we turn it
384             * into a normal node and use the tail to create a new leaf.
385             *
386             * This is a pretty good balance between performance and memory usage.
387             */
388 0           int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
389             {
390             int i;
391             bool is_leaf;
392             node_index idx;
393              
394 0 0         if (os->full) {
395 0           git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full");
396 0           return -1;
397             }
398              
399 0 0         if (text_oid == NULL)
400 0           return os->min_length;
401              
402 0           idx = 0;
403 0           is_leaf = false;
404              
405 0 0         for (i = 0; i < GIT_OID_HEXSZ; ++i) {
406 0           int c = git__fromhex(text_oid[i]);
407             trie_node *node;
408              
409 0 0         if (c == -1) {
410 0           git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - invalid hex value");
411 0           return -1;
412             }
413              
414 0           node = &os->nodes[idx];
415              
416 0 0         if (is_leaf) {
417             const char *tail;
418              
419 0           tail = node->tail;
420 0           node->tail = NULL;
421              
422 0           node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
423 0 0         if (node == NULL) {
424 0 0         if (os->full)
425 0           git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full");
426 0           return -1;
427             }
428             }
429              
430 0 0         if (node->children[c] == 0) {
431 0 0         if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) {
432 0 0         if (os->full)
433 0           git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full");
434 0           return -1;
435             }
436 0           break;
437             }
438              
439 0           idx = node->children[c];
440 0           is_leaf = false;
441              
442 0 0         if (idx < 0) {
443 0           node->children[c] = idx = -idx;
444 0           is_leaf = true;
445             }
446             }
447              
448 0 0         if (++i > os->min_length)
449 0           os->min_length = i;
450              
451 0           return os->min_length;
452             }
453