Commit 882c7742711199f757305687c257ac97262a3a30

Russell Belfer 2014-02-04T10:01:37

Convert pqueue to just be a git_vector This updates the git_pqueue to simply be a set of specialized init/insert/pop functions on a git_vector. To preserve the pqueue feature of having a fixed size heap, I converted the "sorted" field in git_vectors to a more general "flags" field so that pqueue could mix in it's own flag. This had a bunch of ramifications because a number of places were directly looking at the vector "sorted" field - I added a couple new git_vector helpers (is_sorted, set_sorted) so the specific representation of this information could be abstracted.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
diff --git a/src/index.c b/src/index.c
index 7bc5d5b..1ab126c 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1564,7 +1564,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
 	}
 
 	/* entries are guaranteed to be sorted on-disk */
-	index->reuc.sorted = 1;
+	git_vector_set_sorted(&index->reuc, true);
 
 	return 0;
 }
@@ -1610,7 +1610,7 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
 #undef read_conflict_name
 
 	/* entries are guaranteed to be sorted on-disk */
-	index->names.sorted = 1;
+	git_vector_set_sorted(&index->names, true);
 
 	return 0;
 }
@@ -1812,7 +1812,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
 #undef seek_forward
 
 	/* Entries are stored case-sensitively on disk. */
-	index->entries.sorted = !index->ignore_case;
+	git_vector_set_sorted(&index->entries, index->ignore_case);
 	git_vector_sort(&index->entries);
 
 	return 0;
diff --git a/src/pqueue.c b/src/pqueue.c
index ddbad7a..cc31a8f 100644
--- a/src/pqueue.c
+++ b/src/pqueue.c
@@ -15,25 +15,29 @@
 int git_pqueue_init(
 	git_pqueue *pq,
 	uint32_t flags,
-	size_t est_size,
+	size_t init_size,
 	git_vector_cmp cmp)
 {
-	pq->flags = flags;
-	pq->initial_size = est_size;
-	return git_vector_init(&pq->values, est_size, cmp);
-}
+	int error = git_vector_init(pq, init_size, cmp);
 
-void git_pqueue_free(git_pqueue *pq)
-{
-	git_vector_free(&pq->values);
+	if (!error) {
+		/* mix in our flags */
+		pq->flags |= flags;
+
+		/* if fixed size heap, pretend vector is exactly init_size elements */
+		if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0)
+			pq->_alloc_size = init_size;
+	}
+
+	return error;
 }
 
 static void pqueue_up(git_pqueue *pq, size_t el)
 {
 	size_t parent_el = PQUEUE_PARENT_OF(el);
 
-	while (el > 0 && git_vector_cmp_elements(&pq->values, parent_el, el) > 0) {
-		git_vector_swap_elements(&pq->values, el, parent_el);
+	while (el > 0 && git_vector_cmp_elements(pq, parent_el, el) > 0) {
+		git_vector_swap_elements(pq, el, parent_el);
 
 		el = parent_el;
 		parent_el = PQUEUE_PARENT_OF(el);
@@ -42,19 +46,19 @@ static void pqueue_up(git_pqueue *pq, size_t el)
 
 static void pqueue_down(git_pqueue *pq, size_t el)
 {
-	size_t last = git_vector_length(&pq->values);
+	size_t last = git_pqueue_size(pq);
 
 	while (1) {
 		size_t kid = PQUEUE_LCHILD_OF(el), rkid = PQUEUE_RCHILD_OF(el);
 		if (kid >= last)
 			break;
-		if (rkid < last && git_vector_cmp_elements(&pq->values, kid, rkid) > 0)
+		if (rkid < last && git_vector_cmp_elements(pq, kid, rkid) > 0)
 			kid = rkid;
 
-		if (git_vector_cmp_elements(&pq->values, el, kid) < 0)
+		if (git_vector_cmp_elements(pq, el, kid) < 0)
 			break;
 
-		git_vector_swap_elements(&pq->values, el, kid);
+		git_vector_swap_elements(pq, el, kid);
 		el = kid;
 	}
 }
@@ -65,32 +69,33 @@ int git_pqueue_insert(git_pqueue *pq, void *item)
 
 	/* if heap is full, pop the top element if new one should replace it */
 	if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 &&
-		pq->values.length >= pq->initial_size)
+		pq->length >= pq->_alloc_size)
 	{
-		/* skip item if below min item in heap */
-		if (pq->values._cmp(item, git_vector_get(&pq->values, 0)) <= 0)
+		/* skip this item if below min item in heap */
+		if (pq->_cmp(item, git_vector_get(pq, 0)) <= 0)
 			return 0;
+		/* otherwise remove the min item before inserting new */
 		(void)git_pqueue_pop(pq);
 	}
 
-	error = git_vector_insert(&pq->values, item);
-
-	if (!error)
-		pqueue_up(pq, pq->values.length - 1);
+	if (!(error = git_vector_insert(pq, item)))
+		pqueue_up(pq, pq->length - 1);
 
 	return error;
 }
 
 void *git_pqueue_pop(git_pqueue *pq)
 {
-	void *rval = git_vector_get(&pq->values, 0);
+	void *rval = git_pqueue_get(pq, 0);
 
-	if (git_vector_length(&pq->values) > 1) {
-		pq->values.contents[0] = git_vector_last(&pq->values);
-		git_vector_pop(&pq->values);
+	if (git_pqueue_size(pq) > 1) {
+		/* move last item to top of heap, shrink, and push item down */
+		pq->contents[0] = git_vector_last(pq);
+		git_vector_pop(pq);
 		pqueue_down(pq, 0);
 	} else {
-		git_vector_pop(&pq->values);
+		/* all we need to do is shrink the heap in this case */
+		git_vector_pop(pq);
 	}
 
 	return rval;
diff --git a/src/pqueue.h b/src/pqueue.h
index 3c977e1..da7b74e 100644
--- a/src/pqueue.h
+++ b/src/pqueue.h
@@ -9,15 +9,11 @@
 
 #include "vector.h"
 
-typedef struct {
-	git_vector values;
-	size_t     initial_size;
-	uint32_t   flags;
-} git_pqueue;
+typedef git_vector git_pqueue;
 
 enum {
-	GIT_PQUEUE_DEFAULT = 0,
-	GIT_PQUEUE_FIXED_SIZE = (1 << 0), /* don't grow heap, keep highest */
+	/* flag meaning: don't grow heap, keep highest values only */
+	GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1),
 };
 
 /**
@@ -25,36 +21,20 @@ enum {
  *
  * @param pq The priority queue struct to initialize
  * @param flags Flags (see above) to control queue behavior
- * @param est_size The estimated/initial queue size
+ * @param init_size The initial queue size
  * @param cmp The entry priority comparison function
  * @return 0 on success, <0 on error
  */
 extern int git_pqueue_init(
 	git_pqueue *pq,
 	uint32_t flags,
-	size_t est_size,
+	size_t init_size,
 	git_vector_cmp cmp);
 
-/**
- * Free the queue memory
- */
-extern void git_pqueue_free(git_pqueue *pq);
-
-/**
- * Get the number of items in the queue
- */
-GIT_INLINE(size_t) git_pqueue_size(const git_pqueue *pq)
-{
-	return git_vector_length(&pq->values);
-}
-
-/**
- * Get an item in the queue
- */
-GIT_INLINE(void *) git_pqueue_get(const git_pqueue *pq, size_t pos)
-{
-	return git_vector_get(&pq->values, pos);
-}
+#define git_pqueue_free  git_vector_free
+#define git_pqueue_clear git_vector_clear
+#define git_pqueue_size  git_vector_length
+#define git_pqueue_get   git_vector_get
 
 /**
  * Insert a new item into the queue
diff --git a/src/revwalk.c b/src/revwalk.c
index d6dc106..3bfc4d1 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -543,7 +543,7 @@ void git_revwalk_reset(git_revwalk *walk)
 		commit->uninteresting = 0;
 		});
 
-	git_pqueue_free(&walk->iterator_time);
+	git_pqueue_clear(&walk->iterator_time);
 	git_commit_list_free(&walk->iterator_topo);
 	git_commit_list_free(&walk->iterator_rand);
 	git_commit_list_free(&walk->iterator_reverse);
diff --git a/src/sortedcache.c b/src/sortedcache.c
index 466e55d..13f0921 100644
--- a/src/sortedcache.c
+++ b/src/sortedcache.c
@@ -321,7 +321,7 @@ size_t git_sortedcache_entrycount(const git_sortedcache *sc)
 void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
 {
 	/* make sure the items are sorted so this gets the correct item */
-	if (!sc->items.sorted)
+	if (!git_vector_is_sorted(&sc->items))
 		git_vector_sort(&sc->items);
 
 	return git_vector_get(&sc->items, pos);
diff --git a/src/tree.c b/src/tree.c
index 877a3fc..94f779e 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -283,7 +283,8 @@ static const git_tree_entry *entry_fromname(
 {
 	size_t idx;
 
-	assert(tree->entries.sorted); /* be safe when we cast away constness */
+	/* be safe when we cast away constness - i.e. don't trigger a sort */
+	assert(git_vector_is_sorted(&tree->entries));
 
 	if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0)
 		return NULL;
@@ -333,7 +334,8 @@ int git_tree__prefix_position(const git_tree *tree, const char *path)
 	ksearch.filename = path;
 	ksearch.filename_len = strlen(path);
 
-	assert(tree->entries.sorted); /* be safe when we cast away constness */
+	/* be safe when we cast away constness - i.e. don't trigger a sort */
+	assert(git_vector_is_sorted(&tree->entries));
 
 	/* Find tree entry with appropriate prefix */
 	git_vector_bsearch2(
diff --git a/src/vector.c b/src/vector.c
index f0c2f06..e5d8919 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -56,7 +56,9 @@ int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
 	v->_alloc_size = src->length;
 	v->_cmp = cmp;
 	v->length = src->length;
-	v->sorted = src->sorted && cmp == src->_cmp;
+	v->flags  = src->flags;
+	if (cmp != src->_cmp)
+		git_vector_set_sorted(v, 0);
 	v->contents = git__malloc(bytes);
 	GITERR_CHECK_ALLOC(v->contents);
 
@@ -97,7 +99,7 @@ int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
 	v->_alloc_size = 0;
 	v->_cmp = cmp;
 	v->length = 0;
-	v->sorted = 1;
+	v->flags = GIT_VECTOR_SORTED;
 	v->contents = NULL;
 
 	return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
@@ -128,7 +130,8 @@ int git_vector_insert(git_vector *v, void *element)
 		return -1;
 
 	v->contents[v->length++] = element;
-	v->sorted = 0;
+
+	git_vector_set_sorted(v, v->length <= 1);
 
 	return 0;
 }
@@ -141,7 +144,7 @@ int git_vector_insert_sorted(
 
 	assert(v && v->_cmp);
 
-	if (!v->sorted)
+	if (!git_vector_is_sorted(v))
 		git_vector_sort(v);
 
 	if (v->length >= v->_alloc_size &&
@@ -171,11 +174,11 @@ void git_vector_sort(git_vector *v)
 {
 	assert(v);
 
-	if (v->sorted || !v->_cmp)
+	if (git_vector_is_sorted(v) || !v->_cmp)
 		return;
 
 	git__tsort(v->contents, v->length, v->_cmp);
-	v->sorted = 1;
+	git_vector_set_sorted(v, 1);
 }
 
 int git_vector_bsearch2(
@@ -291,7 +294,7 @@ void git_vector_clear(git_vector *v)
 {
 	assert(v);
 	v->length = 0;
-	v->sorted = 1;
+	git_vector_set_sorted(v, 1);
 }
 
 void git_vector_swap(git_vector *a, git_vector *b)
diff --git a/src/vector.h b/src/vector.h
index e8a9678..f983c55 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -11,12 +11,17 @@
 
 typedef int (*git_vector_cmp)(const void *, const void *);
 
+enum {
+	GIT_VECTOR_SORTED = (1u << 0),
+	GIT_VECTOR_FLAG_MAX = (1u << 1),
+};
+
 typedef struct git_vector {
 	size_t _alloc_size;
 	git_vector_cmp _cmp;
 	void **contents;
 	size_t length;
-	int sorted;
+	uint32_t flags;
 } git_vector;
 
 #define GIT_VECTOR_INIT {0}
@@ -86,12 +91,20 @@ void git_vector_remove_matching(
 int git_vector_resize_to(git_vector *v, size_t new_length);
 int git_vector_set(void **old, git_vector *v, size_t position, void *value);
 
+/** Check if vector is sorted */
+#define git_vector_is_sorted(V) (((V)->flags & GIT_VECTOR_SORTED) != 0)
+
+/** Directly set sorted state of vector */
+#define git_vector_set_sorted(V,S) do { \
+	(V)->flags = (S) ? ((V)->flags | GIT_VECTOR_SORTED) : \
+		((V)->flags & ~GIT_VECTOR_SORTED); } while (0)
+
 /** Set the comparison function used for sorting the vector */
 GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
 {
 	if (cmp != v->_cmp) {
 		v->_cmp = cmp;
-		v->sorted = 0;
+		git_vector_set_sorted(v, 0);
 	}
 }
 
diff --git a/tests/index/tests.c b/tests/index/tests.c
index bd90bc5..55a2f2c 100644
--- a/tests/index/tests.c
+++ b/tests/index/tests.c
@@ -80,7 +80,7 @@ void test_index_tests__empty_index(void)
    cl_assert(index->on_disk == 0);
 
    cl_assert(git_index_entrycount(index) == 0);
-   cl_assert(index->entries.sorted);
+   cl_assert(git_vector_is_sorted(&index->entries));
 
    git_index_free(index);
 }
@@ -95,7 +95,7 @@ void test_index_tests__default_test_index(void)
    cl_assert(index->on_disk);
 
    cl_assert(git_index_entrycount(index) == index_entry_count);
-   cl_assert(index->entries.sorted);
+   cl_assert(git_vector_is_sorted(&index->entries));
 
    entries = (git_index_entry **)index->entries.contents;
 
@@ -118,7 +118,7 @@ void test_index_tests__gitgit_index(void)
    cl_assert(index->on_disk);
 
    cl_assert(git_index_entrycount(index) == index_entry_count_2);
-   cl_assert(index->entries.sorted);
+   cl_assert(git_vector_is_sorted(&index->entries));
    cl_assert(index->tree != NULL);
 
    git_index_free(index);
@@ -195,7 +195,7 @@ void test_index_tests__sort1(void)
    cl_git_pass(git_index_open(&index, "fake-index"));
 
    /* FIXME: this test is slightly dumb */
-   cl_assert(index->entries.sorted);
+   cl_assert(git_vector_is_sorted(&index->entries));
 
    git_index_free(index);
 }