source src/pqueue.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
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 "pqueue.h" | ||
9 | - | |||
10 | - | #include "util.h" | ||
11 | - | |||
12 | - | #define PQUEUE_LCHILD_OF(I) (((I)<<1)+1) | ||
13 | - | #define PQUEUE_RCHILD_OF(I) (((I)<<1)+2) | ||
14 | - | #define PQUEUE_PARENT_OF(I) (((I)-1)>>1) | ||
15 | - | |||
16 | 876 | 2 | int git_pqueue_init( | |
17 | - | git_pqueue *pq, | ||
18 | - | uint32_t flags, | ||
19 | - | size_t init_size, | ||
20 | - | git_vector_cmp cmp) | ||
21 | - | { | ||
22 | 876 | 2 | int error = git_vector_init(pq, init_size, cmp); | |
23 | - | |||
24 | 876 | 3 | if (!error) { | |
25 | - | /* mix in our flags */ | ||
26 | 876 | 4 | pq->flags |= flags; | |
27 | - | |||
28 | - | /* if fixed size heap, pretend vector is exactly init_size elements */ | ||
29 | 876 | 4,5 | if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0) | |
30 | 2 | 6 | pq->_alloc_size = init_size; | |
31 | - | } | ||
32 | - | |||
33 | 876 | 7 | return error; | |
34 | - | } | ||
35 | - | |||
36 | 4242 | 2 | static void pqueue_up(git_pqueue *pq, size_t el) | |
37 | - | { | ||
38 | 4242 | 2 | size_t parent_el = PQUEUE_PARENT_OF(el); | |
39 | 4242 | 2 | void *kid = git_vector_get(pq, el); | |
40 | - | |||
41 | 6644 | 3,8 | while (el > 0) { | |
42 | 4821 | 4 | void *parent = pq->contents[parent_el]; | |
43 | - | |||
44 | 4821 | 4,5 | if (pq->_cmp(parent, kid) <= 0) | |
45 | 2419 | 6 | break; | |
46 | - | |||
47 | 2402 | 7 | pq->contents[el] = parent; | |
48 | - | |||
49 | 2402 | 7 | el = parent_el; | |
50 | 2402 | 7 | parent_el = PQUEUE_PARENT_OF(el); | |
51 | - | } | ||
52 | - | |||
53 | 4242 | 9 | pq->contents[el] = kid; | |
54 | 4242 | 9 | } | |
55 | - | |||
56 | 3608 | 2 | static void pqueue_down(git_pqueue *pq, size_t el) | |
57 | - | { | ||
58 | 3608 | 2 | void *parent = git_vector_get(pq, el), *kid, *rkid; | |
59 | - | |||
60 | - | while (1) { | ||
61 | 6668 | 3 | size_t kid_el = PQUEUE_LCHILD_OF(el); | |
62 | - | |||
63 | 6668 | 3,4 | if ((kid = git_vector_get(pq, kid_el)) == NULL) | |
64 | 2762 | 5 | break; | |
65 | - | |||
66 | 3906 | 6,7,9 | if ((rkid = git_vector_get(pq, kid_el + 1)) != NULL && | |
67 | 2964 | 8 | pq->_cmp(kid, rkid) > 0) { | |
68 | 1249 | 10 | kid = rkid; | |
69 | 1249 | 10 | kid_el += 1; | |
70 | - | } | ||
71 | - | |||
72 | 3906 | 11,12 | if (pq->_cmp(parent, kid) <= 0) | |
73 | 846 | 13 | break; | |
74 | - | |||
75 | 3060 | 14 | pq->contents[el] = kid; | |
76 | 3060 | 14 | el = kid_el; | |
77 | 3060 | 14 | } | |
78 | - | |||
79 | 3608 | 15 | pq->contents[el] = parent; | |
80 | 3608 | 15 | } | |
81 | - | |||
82 | 4383 | 2 | int git_pqueue_insert(git_pqueue *pq, void *item) | |
83 | - | { | ||
84 | 4383 | 2 | int error = 0; | |
85 | - | |||
86 | - | /* if heap is full, pop the top element if new one should replace it */ | ||
87 | 4383 | 2,3 | if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 && | |
88 | 200 | 3 | pq->length >= pq->_alloc_size) | |
89 | - | { | ||
90 | - | /* skip this item if below min item in heap or if | ||
91 | - | * we do not have a comparison function */ | ||
92 | 100 | 4-7 | if (!pq->_cmp || pq->_cmp(item, git_vector_get(pq, 0)) <= 0) | |
93 | 67 | 8 | return 0; | |
94 | - | /* otherwise remove the min item before inserting new */ | ||
95 | 33 | 9 | (void)git_pqueue_pop(pq); | |
96 | - | } | ||
97 | - | |||
98 | 4316 | 10-12 | if (!(error = git_vector_insert(pq, item)) && pq->_cmp) | |
99 | 4242 | 13 | pqueue_up(pq, pq->length - 1); | |
100 | - | |||
101 | 4316 | 14 | return error; | |
102 | - | } | ||
103 | - | |||
104 | 4191 | 2 | void *git_pqueue_pop(git_pqueue *pq) | |
105 | - | { | ||
106 | - | void *rval; | ||
107 | - | |||
108 | 4191 | 2 | if (!pq->_cmp) { | |
109 | 80 | 3 | rval = git_vector_last(pq); | |
110 | - | } else { | ||
111 | 4111 | 4 | rval = git_pqueue_get(pq, 0); | |
112 | - | } | ||
113 | - | |||
114 | 4191 | 5-7 | if (git_pqueue_size(pq) > 1 && pq->_cmp) { | |
115 | - | /* move last item to top of heap, shrink, and push item down */ | ||
116 | 3608 | 8 | pq->contents[0] = git_vector_last(pq); | |
117 | 3608 | 9 | git_vector_pop(pq); | |
118 | 3608 | 10 | pqueue_down(pq, 0); | |
119 | - | } else { | ||
120 | - | /* all we need to do is shrink the heap in this case */ | ||
121 | 583 | 11 | git_vector_pop(pq); | |
122 | - | } | ||
123 | - | |||
124 | 4191 | 12 | return rval; | |
125 | - | } |