1 /*
2 * sorts.c: all sorts of sorts
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25
26 #include <apr_pools.h>
27 #include <apr_hash.h>
28 #include <apr_tables.h>
29 #include <stdlib.h> /* for qsort() */
30 #include <assert.h>
31 #include "svn_hash.h"
32 #include "svn_path.h"
33 #include "svn_sorts.h"
34 #include "svn_error.h"
35 #include "private/svn_sorts_private.h"
36
37 #include "svn_private_config.h"
38
39
40
41 /*** svn_sort__hash() ***/
42
43 /* (Should this be a permanent part of APR?)
44
45 OK, folks, here's what's going on. APR hash tables hash on
46 key/klen objects, and store associated generic values. They work
47 great, but they have no ordering.
48
49 The point of this exercise is to somehow arrange a hash's keys into
50 an "ordered list" of some kind -- in this case, a nicely sorted
51 one.
52
53 We're using APR arrays, therefore, because that's what they are:
54 ordered lists. However, what "keys" should we put in the array?
55 Clearly, (const char *) objects aren't general enough. Or rather,
56 they're not as general as APR's hash implementation, which stores
57 (void *)/length as keys. We don't want to lose this information.
58
59 Therefore, it makes sense to store pointers to {void *, size_t}
60 structures in our array. No such apr object exists... BUT... if we
61 can use a new type svn_sort__item_t which contains {char *, size_t, void
62 *}. If store these objects in our array, we get the hash value
63 *for free*. When looping over the final array, we don't need to
64 call apr_hash_get(). Major bonus!
65 */
66
67
68 int
svn_sort_compare_items_as_paths(const svn_sort__item_t * a,const svn_sort__item_t * b)69 svn_sort_compare_items_as_paths(const svn_sort__item_t *a,
70 const svn_sort__item_t *b)
71 {
72 const char *astr, *bstr;
73
74 astr = a->key;
75 bstr = b->key;
76 assert(astr[a->klen] == '\0');
77 assert(bstr[b->klen] == '\0');
78 return svn_path_compare_paths(astr, bstr);
79 }
80
81
82 int
svn_sort_compare_items_lexically(const svn_sort__item_t * a,const svn_sort__item_t * b)83 svn_sort_compare_items_lexically(const svn_sort__item_t *a,
84 const svn_sort__item_t *b)
85 {
86 int val;
87 apr_size_t len;
88
89 /* Compare bytes of a's key and b's key up to the common length. */
90 len = (a->klen < b->klen) ? a->klen : b->klen;
91 val = memcmp(a->key, b->key, len);
92 if (val != 0)
93 return val;
94
95 /* They match up until one of them ends; whichever is longer is greater. */
96 return (a->klen < b->klen) ? -1 : (a->klen > b->klen) ? 1 : 0;
97 }
98
99
100 int
svn_sort_compare_revisions(const void * a,const void * b)101 svn_sort_compare_revisions(const void *a, const void *b)
102 {
103 svn_revnum_t a_rev = *(const svn_revnum_t *)a;
104 svn_revnum_t b_rev = *(const svn_revnum_t *)b;
105
106 if (a_rev == b_rev)
107 return 0;
108
109 return a_rev < b_rev ? 1 : -1;
110 }
111
112
113 int
svn_sort_compare_paths(const void * a,const void * b)114 svn_sort_compare_paths(const void *a, const void *b)
115 {
116 const char *item1 = *((const char * const *) a);
117 const char *item2 = *((const char * const *) b);
118
119 return svn_path_compare_paths(item1, item2);
120 }
121
122
123 int
svn_sort_compare_ranges(const void * a,const void * b)124 svn_sort_compare_ranges(const void *a, const void *b)
125 {
126 const svn_merge_range_t *item1 = *((const svn_merge_range_t * const *) a);
127 const svn_merge_range_t *item2 = *((const svn_merge_range_t * const *) b);
128
129 if (item1->start == item2->start
130 && item1->end == item2->end)
131 return 0;
132
133 if (item1->start == item2->start)
134 return item1->end < item2->end ? -1 : 1;
135
136 return item1->start < item2->start ? -1 : 1;
137 }
138
139 void
svn_sort__array(apr_array_header_t * array,int (* comparison_func)(const void *,const void *))140 svn_sort__array(apr_array_header_t *array,
141 int (*comparison_func)(const void *,
142 const void *))
143 {
144 qsort(array->elts, array->nelts, array->elt_size, comparison_func);
145 }
146
147 apr_array_header_t *
svn_sort__hash(apr_hash_t * ht,int (* comparison_func)(const svn_sort__item_t *,const svn_sort__item_t *),apr_pool_t * pool)148 svn_sort__hash(apr_hash_t *ht,
149 int (*comparison_func)(const svn_sort__item_t *,
150 const svn_sort__item_t *),
151 apr_pool_t *pool)
152 {
153 apr_hash_index_t *hi;
154 apr_array_header_t *ary;
155 svn_boolean_t sorted;
156 svn_sort__item_t *prev_item;
157
158 /* allocate an array with enough elements to hold all the keys. */
159 ary = apr_array_make(pool, apr_hash_count(ht), sizeof(svn_sort__item_t));
160
161 /* loop over hash table and push all keys into the array */
162 sorted = TRUE;
163 prev_item = NULL;
164 for (hi = apr_hash_first(pool, ht); hi; hi = apr_hash_next(hi))
165 {
166 svn_sort__item_t *item = apr_array_push(ary);
167
168 apr_hash_this(hi, &item->key, &item->klen, &item->value);
169
170 if (prev_item == NULL)
171 {
172 prev_item = item;
173 continue;
174 }
175
176 if (sorted)
177 {
178 sorted = (comparison_func(prev_item, item) < 0);
179 prev_item = item;
180 }
181 }
182
183 /* quicksort the array if it isn't already sorted. */
184 if (!sorted)
185 svn_sort__array(ary,
186 (int (*)(const void *, const void *))comparison_func);
187
188 return ary;
189 }
190
191 /* Return the lowest index at which the element *KEY should be inserted into
192 the array at BASE which has NELTS elements of size ELT_SIZE bytes each,
193 according to the ordering defined by COMPARE_FUNC.
194 0 <= NELTS <= INT_MAX, 1 <= ELT_SIZE <= INT_MAX.
195 The array must already be sorted in the ordering defined by COMPARE_FUNC.
196 COMPARE_FUNC is defined as for the C stdlib function bsearch().
197 Note: This function is modeled on bsearch() and on lower_bound() in the
198 C++ STL.
199 */
200 static int
bsearch_lower_bound(const void * key,const void * base,int nelts,int elt_size,int (* compare_func)(const void *,const void *))201 bsearch_lower_bound(const void *key,
202 const void *base,
203 int nelts,
204 int elt_size,
205 int (*compare_func)(const void *, const void *))
206 {
207 int lower = 0;
208 int upper = nelts - 1;
209
210 /* Binary search for the lowest position at which to insert KEY. */
211 while (lower <= upper)
212 {
213 int try = lower + (upper - lower) / 2; /* careful to avoid overflow */
214 int cmp = compare_func((const char *)base + try * elt_size, key);
215
216 if (cmp < 0)
217 lower = try + 1;
218 else
219 upper = try - 1;
220 }
221 assert(lower == upper + 1);
222
223 return lower;
224 }
225
226 int
svn_sort__bsearch_lower_bound(const apr_array_header_t * array,const void * key,int (* compare_func)(const void *,const void *))227 svn_sort__bsearch_lower_bound(const apr_array_header_t *array,
228 const void *key,
229 int (*compare_func)(const void *, const void *))
230 {
231 return bsearch_lower_bound(key,
232 array->elts, array->nelts, array->elt_size,
233 compare_func);
234 }
235
236 void *
svn_sort__array_lookup(const apr_array_header_t * array,const void * key,int * hint,int (* compare_func)(const void *,const void *))237 svn_sort__array_lookup(const apr_array_header_t *array,
238 const void *key,
239 int *hint,
240 int (*compare_func)(const void *, const void *))
241 {
242 void *result;
243 int idx;
244
245 /* If provided, try the index following *HINT (i.e. probably the last
246 * hit location) first. This speeds up linear scans. */
247 if (hint)
248 {
249 /* We intend to insert right behind *HINT.
250 * Exit this function early, if we actually can. */
251 idx = *hint + 1;
252 if (idx >= array->nelts)
253 {
254 /* We intend to insert after the last entry.
255 * That is only allowed if that last entry is smaller than KEY.
256 * In that case, there will be no current entry, i.e. we must
257 * return NULL. */
258 apr_size_t offset;
259
260 *hint = array->nelts;
261 if (array->nelts == 0)
262 return NULL;
263
264 offset = (array->nelts - 1) * array->elt_size;
265 if (compare_func(array->elts + offset, key) < 0)
266 return NULL;
267 }
268 else if (idx > 0)
269 {
270 /* Intend to insert at a position inside the array, i.e. not
271 * at one of the boundaries. The predecessor must be smaller
272 * and the current entry at IDX must be larger than KEY. */
273 void *previous;
274
275 *hint = idx;
276 previous = array->elts + (idx-1) * array->elt_size;
277 result = array->elts + idx * array->elt_size;
278 if (compare_func(previous, key) && !compare_func(result, key))
279 return result;
280 }
281 else if (idx <= 0)
282 {
283 /* Intend to insert at the beginning of an non-empty array.
284 * That requires the first entry to be larger than KEY. */
285 *hint = 0;
286 if (!compare_func(array->elts, key))
287 return array->elts;
288 }
289
290 /* The HINT did not help. */
291 }
292
293 idx = bsearch_lower_bound(key, array->elts, array->nelts, array->elt_size,
294 compare_func);
295 if (hint)
296 *hint = idx;
297 if (idx >= array->nelts)
298 return NULL;
299
300 result = array->elts + idx * array->elt_size;
301 return compare_func(result, key) ? NULL : result;
302 }
303
304 svn_error_t *
svn_sort__array_insert2(apr_array_header_t * array,const void * new_element,int insert_index)305 svn_sort__array_insert2(apr_array_header_t *array,
306 const void *new_element,
307 int insert_index)
308 {
309 int elements_to_move;
310 char *new_position;
311
312 if (insert_index < 0 || insert_index > array->nelts)
313 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
314 _("svn_sort__array_insert2: Attempted insert "
315 "at index %d in array length %d"),
316 insert_index, array->nelts);
317
318 elements_to_move = array->nelts - insert_index; /* before bumping nelts */
319
320 /* Grow the array, allocating a new space at the end. Note: this can
321 reallocate the array's "elts" at a different address. */
322 apr_array_push(array);
323
324 /* Move the elements after INSERT_INDEX along. (When elements_to_move == 0,
325 this is a no-op.) */
326 new_position = (char *)array->elts + insert_index * array->elt_size;
327 memmove(new_position + array->elt_size, new_position,
328 array->elt_size * elements_to_move);
329
330 /* Copy in the new element */
331 memcpy(new_position, new_element, array->elt_size);
332 return SVN_NO_ERROR;
333 }
334
335 svn_error_t *
svn_sort__array_delete2(apr_array_header_t * arr,int delete_index,int elements_to_delete)336 svn_sort__array_delete2(apr_array_header_t *arr,
337 int delete_index,
338 int elements_to_delete)
339 {
340 if (!(delete_index >= 0
341 && delete_index < arr->nelts
342 && elements_to_delete > 0
343 && (arr->nelts - delete_index) >= elements_to_delete))
344 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
345 _("svn_sort__array_delete2: Attempted delete "
346 "at index %d, %d elements, in array length %d"),
347 delete_index, elements_to_delete, arr->nelts);
348
349 /* If we are deleting a block of elements that does not extend to the end
350 of the array, then we need to move the remaining elements to keep
351 the array contiguous. */
352 if ((elements_to_delete + delete_index) < arr->nelts)
353 memmove(
354 arr->elts + arr->elt_size * delete_index,
355 arr->elts + (arr->elt_size * (delete_index + elements_to_delete)),
356 arr->elt_size * (arr->nelts - elements_to_delete - delete_index));
357
358 /* Delete the last ELEMENTS_TO_DELETE elements. */
359 arr->nelts -= elements_to_delete;
360 return SVN_NO_ERROR;
361 }
362
363 void
svn_sort__array_reverse(apr_array_header_t * array,apr_pool_t * scratch_pool)364 svn_sort__array_reverse(apr_array_header_t *array,
365 apr_pool_t *scratch_pool)
366 {
367 int i;
368
369 if (array->elt_size == sizeof(void *))
370 {
371 for (i = 0; i < array->nelts / 2; i++)
372 {
373 int swap_index = array->nelts - i - 1;
374 void *tmp = APR_ARRAY_IDX(array, i, void *);
375
376 APR_ARRAY_IDX(array, i, void *) =
377 APR_ARRAY_IDX(array, swap_index, void *);
378 APR_ARRAY_IDX(array, swap_index, void *) = tmp;
379 }
380 }
381 else
382 {
383 apr_size_t sz = array->elt_size;
384 char *tmp = apr_palloc(scratch_pool, sz);
385
386 for (i = 0; i < array->nelts / 2; i++)
387 {
388 int swap_index = array->nelts - i - 1;
389 char *x = array->elts + (sz * i);
390 char *y = array->elts + (sz * swap_index);
391
392 memcpy(tmp, x, sz);
393 memcpy(x, y, sz);
394 memcpy(y, tmp, sz);
395 }
396 }
397 }
398
399 /* Our priority queue data structure:
400 * Simply remember the constructor parameters.
401 */
402 struct svn_priority_queue__t
403 {
404 /* the queue elements, ordered as a heap according to COMPARE_FUNC */
405 apr_array_header_t *elements;
406
407 /* predicate used to order the heap */
408 int (*compare_func)(const void *, const void *);
409 };
410
411 /* Return TRUE, if heap element number LHS in QUEUE is smaller than element
412 * number RHS according to QUEUE->COMPARE_FUNC
413 */
414 static int
heap_is_less(svn_priority_queue__t * queue,apr_size_t lhs,apr_size_t rhs)415 heap_is_less(svn_priority_queue__t *queue,
416 apr_size_t lhs,
417 apr_size_t rhs)
418 {
419 char *lhs_value = queue->elements->elts + lhs * queue->elements->elt_size;
420 char *rhs_value = queue->elements->elts + rhs * queue->elements->elt_size;
421
422 /* nelts is never negative */
423 assert(lhs < (apr_size_t)queue->elements->nelts);
424 assert(rhs < (apr_size_t)queue->elements->nelts);
425 return queue->compare_func(lhs_value, rhs_value) < 0;
426 }
427
428 /* Exchange elements number LHS and RHS in QUEUE.
429 */
430 static void
heap_swap(svn_priority_queue__t * queue,apr_size_t lhs,apr_size_t rhs)431 heap_swap(svn_priority_queue__t *queue,
432 apr_size_t lhs,
433 apr_size_t rhs)
434 {
435 int i;
436 char *lhs_value = queue->elements->elts + lhs * queue->elements->elt_size;
437 char *rhs_value = queue->elements->elts + rhs * queue->elements->elt_size;
438
439 for (i = 0; i < queue->elements->elt_size; ++i)
440 {
441 char temp = lhs_value[i];
442 lhs_value[i] = rhs_value[i];
443 rhs_value[i] = temp;
444 }
445 }
446
447 /* Move element number IDX to lower indexes until the heap criterion is
448 * fulfilled again.
449 */
450 static void
heap_bubble_down(svn_priority_queue__t * queue,int idx)451 heap_bubble_down(svn_priority_queue__t *queue,
452 int idx)
453 {
454 while (idx > 0 && heap_is_less(queue, idx, (idx - 1) / 2))
455 {
456 heap_swap(queue, idx, (idx - 1) / 2);
457 idx = (idx - 1) / 2;
458 }
459 }
460
461 /* Move element number IDX to higher indexes until the heap criterion is
462 * fulfilled again.
463 */
464 static void
heap_bubble_up(svn_priority_queue__t * queue,int idx)465 heap_bubble_up(svn_priority_queue__t *queue,
466 int idx)
467 {
468 while (2 * idx + 2 < queue->elements->nelts)
469 {
470 int child = heap_is_less(queue, 2 * idx + 1, 2 * idx + 2)
471 ? 2 * idx + 1
472 : 2 * idx + 2;
473
474 if (heap_is_less(queue, idx, child))
475 return;
476
477 heap_swap(queue, idx, child);
478 idx = child;
479 }
480
481 if ( 2 * idx + 1 < queue->elements->nelts
482 && heap_is_less(queue, 2 * idx + 1, idx))
483 heap_swap(queue, 2 * idx + 1, idx);
484 }
485
486 svn_priority_queue__t *
svn_priority_queue__create(apr_array_header_t * elements,int (* compare_func)(const void *,const void *))487 svn_priority_queue__create(apr_array_header_t *elements,
488 int (*compare_func)(const void *, const void *))
489 {
490 int i;
491
492 svn_priority_queue__t *queue = apr_pcalloc(elements->pool, sizeof(*queue));
493 queue->elements = elements;
494 queue->compare_func = compare_func;
495
496 for (i = elements->nelts / 2; i >= 0; --i)
497 heap_bubble_up(queue, i);
498
499 return queue;
500 }
501
502 apr_size_t
svn_priority_queue__size(svn_priority_queue__t * queue)503 svn_priority_queue__size(svn_priority_queue__t *queue)
504 {
505 return queue->elements->nelts;
506 }
507
508 void *
svn_priority_queue__peek(svn_priority_queue__t * queue)509 svn_priority_queue__peek(svn_priority_queue__t *queue)
510 {
511 return queue->elements->nelts ? queue->elements->elts : NULL;
512 }
513
514 void
svn_priority_queue__update(svn_priority_queue__t * queue)515 svn_priority_queue__update(svn_priority_queue__t *queue)
516 {
517 heap_bubble_up(queue, 0);
518 }
519
520 void
svn_priority_queue__pop(svn_priority_queue__t * queue)521 svn_priority_queue__pop(svn_priority_queue__t *queue)
522 {
523 if (queue->elements->nelts)
524 {
525 memmove(queue->elements->elts,
526 queue->elements->elts
527 + (queue->elements->nelts - 1) * queue->elements->elt_size,
528 queue->elements->elt_size);
529 --queue->elements->nelts;
530 heap_bubble_up(queue, 0);
531 }
532 }
533
534 void
svn_priority_queue__push(svn_priority_queue__t * queue,const void * element)535 svn_priority_queue__push(svn_priority_queue__t *queue,
536 const void *element)
537 {
538 /* we cannot duplicate elements due to potential array re-allocs */
539 assert(element && element != queue->elements->elts);
540
541 memcpy(apr_array_push(queue->elements), element, queue->elements->elt_size);
542 heap_bubble_down(queue, queue->elements->nelts - 1);
543 }
544