File Coverage

deps/libgit2/src/libgit2/oid.c
Criterion Covered Total %
statement 89 189 47.0
branch 33 100 33.0
condition n/a
subroutine n/a
pod n/a
total 122 289 42.2


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