2 * sorts.c: all sorts of sorts
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
26 #include <apr_pools.h>
28 #include <apr_tables.h>
29 #include <stdlib.h> /* for qsort() */
33 #include "svn_sorts.h"
34 #include "svn_error.h"
35 #include "private/svn_sorts_private.h"
39 /*** svn_sort__hash() ***/
41 /* (Should this be a permanent part of APR?)
43 OK, folks, here's what's going on. APR hash tables hash on
44 key/klen objects, and store associated generic values. They work
45 great, but they have no ordering.
47 The point of this exercise is to somehow arrange a hash's keys into
48 an "ordered list" of some kind -- in this case, a nicely sorted
51 We're using APR arrays, therefore, because that's what they are:
52 ordered lists. However, what "keys" should we put in the array?
53 Clearly, (const char *) objects aren't general enough. Or rather,
54 they're not as general as APR's hash implementation, which stores
55 (void *)/length as keys. We don't want to lose this information.
57 Therefore, it makes sense to store pointers to {void *, size_t}
58 structures in our array. No such apr object exists... BUT... if we
59 can use a new type svn_sort__item_t which contains {char *, size_t, void
60 *}. If store these objects in our array, we get the hash value
61 *for free*. When looping over the final array, we don't need to
62 call apr_hash_get(). Major bonus!
67 svn_sort_compare_items_as_paths(const svn_sort__item_t
*a
,
68 const svn_sort__item_t
*b
)
70 const char *astr
, *bstr
;
74 assert(astr
[a
->klen
] == '\0');
75 assert(bstr
[b
->klen
] == '\0');
76 return svn_path_compare_paths(astr
, bstr
);
81 svn_sort_compare_items_lexically(const svn_sort__item_t
*a
,
82 const svn_sort__item_t
*b
)
87 /* Compare bytes of a's key and b's key up to the common length. */
88 len
= (a
->klen
< b
->klen
) ? a
->klen
: b
->klen
;
89 val
= memcmp(a
->key
, b
->key
, len
);
93 /* They match up until one of them ends; whichever is longer is greater. */
94 return (a
->klen
< b
->klen
) ? -1 : (a
->klen
> b
->klen
) ? 1 : 0;
99 svn_sort_compare_revisions(const void *a
, const void *b
)
101 svn_revnum_t a_rev
= *(const svn_revnum_t
*)a
;
102 svn_revnum_t b_rev
= *(const svn_revnum_t
*)b
;
107 return a_rev
< b_rev
? 1 : -1;
112 svn_sort_compare_paths(const void *a
, const void *b
)
114 const char *item1
= *((const char * const *) a
);
115 const char *item2
= *((const char * const *) b
);
117 return svn_path_compare_paths(item1
, item2
);
122 svn_sort_compare_ranges(const void *a
, const void *b
)
124 const svn_merge_range_t
*item1
= *((const svn_merge_range_t
* const *) a
);
125 const svn_merge_range_t
*item2
= *((const svn_merge_range_t
* const *) b
);
127 if (item1
->start
== item2
->start
128 && item1
->end
== item2
->end
)
131 if (item1
->start
== item2
->start
)
132 return item1
->end
< item2
->end
? -1 : 1;
134 return item1
->start
< item2
->start
? -1 : 1;
138 svn_sort__array(apr_array_header_t
*array
,
139 int (*comparison_func
)(const void *,
142 qsort(array
->elts
, array
->nelts
, array
->elt_size
, comparison_func
);
146 svn_sort__hash(apr_hash_t
*ht
,
147 int (*comparison_func
)(const svn_sort__item_t
*,
148 const svn_sort__item_t
*),
151 apr_hash_index_t
*hi
;
152 apr_array_header_t
*ary
;
153 svn_boolean_t sorted
;
154 svn_sort__item_t
*prev_item
;
156 /* allocate an array with enough elements to hold all the keys. */
157 ary
= apr_array_make(pool
, apr_hash_count(ht
), sizeof(svn_sort__item_t
));
159 /* loop over hash table and push all keys into the array */
162 for (hi
= apr_hash_first(pool
, ht
); hi
; hi
= apr_hash_next(hi
))
164 svn_sort__item_t
*item
= apr_array_push(ary
);
166 apr_hash_this(hi
, &item
->key
, &item
->klen
, &item
->value
);
168 if (prev_item
== NULL
)
176 sorted
= (comparison_func(prev_item
, item
) < 0);
181 /* quicksort the array if it isn't already sorted. */
184 (int (*)(const void *, const void *))comparison_func
);
189 /* Return the lowest index at which the element *KEY should be inserted into
190 the array at BASE which has NELTS elements of size ELT_SIZE bytes each,
191 according to the ordering defined by COMPARE_FUNC.
192 0 <= NELTS <= INT_MAX, 1 <= ELT_SIZE <= INT_MAX.
193 The array must already be sorted in the ordering defined by COMPARE_FUNC.
194 COMPARE_FUNC is defined as for the C stdlib function bsearch().
195 Note: This function is modeled on bsearch() and on lower_bound() in the
199 bsearch_lower_bound(const void *key
,
203 int (*compare_func
)(const void *, const void *))
206 int upper
= nelts
- 1;
208 /* Binary search for the lowest position at which to insert KEY. */
209 while (lower
<= upper
)
211 int try = lower
+ (upper
- lower
) / 2; /* careful to avoid overflow */
212 int cmp
= compare_func((const char *)base
+ try * elt_size
, key
);
219 assert(lower
== upper
+ 1);
225 svn_sort__bsearch_lower_bound(const apr_array_header_t
*array
,
227 int (*compare_func
)(const void *, const void *))
229 return bsearch_lower_bound(key
,
230 array
->elts
, array
->nelts
, array
->elt_size
,
235 svn_sort__array_lookup(const apr_array_header_t
*array
,
238 int (*compare_func
)(const void *, const void *))
243 /* If provided, try the index following *HINT (i.e. probably the last
244 * hit location) first. This speeds up linear scans. */
247 /* We intend to insert right behind *HINT.
248 * Exit this function early, if we actually can. */
250 if (idx
>= array
->nelts
)
252 /* We intend to insert after the last entry.
253 * That is only allowed if that last entry is smaller than KEY.
254 * In that case, there will be no current entry, i.e. we must
258 *hint
= array
->nelts
;
259 if (array
->nelts
== 0)
262 offset
= (array
->nelts
- 1) * array
->elt_size
;
263 if (compare_func(array
->elts
+ offset
, key
) < 0)
268 /* Intend to insert at a position inside the array, i.e. not
269 * at one of the boundaries. The predecessor must be smaller
270 * and the current entry at IDX must be larger than KEY. */
274 previous
= array
->elts
+ (idx
-1) * array
->elt_size
;
275 result
= array
->elts
+ idx
* array
->elt_size
;
276 if (compare_func(previous
, key
) && !compare_func(result
, key
))
281 /* Intend to insert at the beginning of an non-empty array.
282 * That requires the first entry to be larger than KEY. */
284 if (!compare_func(array
->elts
, key
))
288 /* The HINT did not help. */
291 idx
= bsearch_lower_bound(key
, array
->elts
, array
->nelts
, array
->elt_size
,
295 if (idx
>= array
->nelts
)
298 result
= array
->elts
+ idx
* array
->elt_size
;
299 return compare_func(result
, key
) ? NULL
: result
;
303 svn_sort__array_insert(apr_array_header_t
*array
,
304 const void *new_element
,
307 int elements_to_move
;
310 assert(0 <= insert_index
&& insert_index
<= array
->nelts
);
311 elements_to_move
= array
->nelts
- insert_index
; /* before bumping nelts */
313 /* Grow the array, allocating a new space at the end. Note: this can
314 reallocate the array's "elts" at a different address. */
315 apr_array_push(array
);
317 /* Move the elements after INSERT_INDEX along. (When elements_to_move == 0,
319 new_position
= (char *)array
->elts
+ insert_index
* array
->elt_size
;
320 memmove(new_position
+ array
->elt_size
, new_position
,
321 array
->elt_size
* elements_to_move
);
323 /* Copy in the new element */
324 memcpy(new_position
, new_element
, array
->elt_size
);
328 svn_sort__array_delete(apr_array_header_t
*arr
,
330 int elements_to_delete
)
332 /* Do we have a valid index and are there enough elements? */
333 if (delete_index
>= 0
334 && delete_index
< arr
->nelts
335 && elements_to_delete
> 0
336 && (arr
->nelts
- delete_index
) >= elements_to_delete
)
338 /* If we are not deleting a block of elements that extends to the end
339 of the array, then we need to move the remaining elements to keep
340 the array contiguous. */
341 if ((elements_to_delete
+ delete_index
) < arr
->nelts
)
343 arr
->elts
+ arr
->elt_size
* delete_index
,
344 arr
->elts
+ (arr
->elt_size
* (delete_index
+ elements_to_delete
)),
345 arr
->elt_size
* (arr
->nelts
- elements_to_delete
- delete_index
));
347 /* Delete the last ELEMENTS_TO_DELETE elements. */
348 arr
->nelts
-= elements_to_delete
;
353 svn_sort__array_reverse(apr_array_header_t
*array
,
354 apr_pool_t
*scratch_pool
)
358 if (array
->elt_size
== sizeof(void *))
360 for (i
= 0; i
< array
->nelts
/ 2; i
++)
362 int swap_index
= array
->nelts
- i
- 1;
363 void *tmp
= APR_ARRAY_IDX(array
, i
, void *);
365 APR_ARRAY_IDX(array
, i
, void *) =
366 APR_ARRAY_IDX(array
, swap_index
, void *);
367 APR_ARRAY_IDX(array
, swap_index
, void *) = tmp
;
372 apr_size_t sz
= array
->elt_size
;
373 char *tmp
= apr_palloc(scratch_pool
, sz
);
375 for (i
= 0; i
< array
->nelts
/ 2; i
++)
377 int swap_index
= array
->nelts
- i
- 1;
378 char *x
= array
->elts
+ (sz
* i
);
379 char *y
= array
->elts
+ (sz
* swap_index
);
388 /* Our priority queue data structure:
389 * Simply remember the constructor parameters.
391 struct svn_priority_queue__t
393 /* the queue elements, ordered as a heap according to COMPARE_FUNC */
394 apr_array_header_t
*elements
;
396 /* predicate used to order the heap */
397 int (*compare_func
)(const void *, const void *);
400 /* Return TRUE, if heap element number LHS in QUEUE is smaller than element
401 * number RHS according to QUEUE->COMPARE_FUNC
404 heap_is_less(svn_priority_queue__t
*queue
,
408 char *lhs_value
= queue
->elements
->elts
+ lhs
* queue
->elements
->elt_size
;
409 char *rhs_value
= queue
->elements
->elts
+ rhs
* queue
->elements
->elt_size
;
411 /* nelts is never negative */
412 assert(lhs
< (apr_size_t
)queue
->elements
->nelts
);
413 assert(rhs
< (apr_size_t
)queue
->elements
->nelts
);
414 return queue
->compare_func(lhs_value
, rhs_value
) < 0;
417 /* Exchange elements number LHS and RHS in QUEUE.
420 heap_swap(svn_priority_queue__t
*queue
,
425 char *lhs_value
= queue
->elements
->elts
+ lhs
* queue
->elements
->elt_size
;
426 char *rhs_value
= queue
->elements
->elts
+ rhs
* queue
->elements
->elt_size
;
428 for (i
= 0; i
< queue
->elements
->elt_size
; ++i
)
430 char temp
= lhs_value
[i
];
431 lhs_value
[i
] = rhs_value
[i
];
436 /* Move element number IDX to lower indexes until the heap criterion is
440 heap_bubble_down(svn_priority_queue__t
*queue
,
443 while (idx
> 0 && heap_is_less(queue
, idx
, (idx
- 1) / 2))
445 heap_swap(queue
, idx
, (idx
- 1) / 2);
450 /* Move element number IDX to higher indexes until the heap criterion is
454 heap_bubble_up(svn_priority_queue__t
*queue
,
457 while (2 * idx
+ 2 < queue
->elements
->nelts
)
459 int child
= heap_is_less(queue
, 2 * idx
+ 1, 2 * idx
+ 2)
463 if (heap_is_less(queue
, idx
, child
))
466 heap_swap(queue
, idx
, child
);
470 if ( 2 * idx
+ 1 < queue
->elements
->nelts
471 && heap_is_less(queue
, 2 * idx
+ 1, idx
))
472 heap_swap(queue
, 2 * idx
+ 1, idx
);
475 svn_priority_queue__t
*
476 svn_priority_queue__create(apr_array_header_t
*elements
,
477 int (*compare_func
)(const void *, const void *))
481 svn_priority_queue__t
*queue
= apr_pcalloc(elements
->pool
, sizeof(*queue
));
482 queue
->elements
= elements
;
483 queue
->compare_func
= compare_func
;
485 for (i
= elements
->nelts
/ 2; i
>= 0; --i
)
486 heap_bubble_up(queue
, i
);
492 svn_priority_queue__size(svn_priority_queue__t
*queue
)
494 return queue
->elements
->nelts
;
498 svn_priority_queue__peek(svn_priority_queue__t
*queue
)
500 return queue
->elements
->nelts
? queue
->elements
->elts
: NULL
;
504 svn_priority_queue__update(svn_priority_queue__t
*queue
)
506 heap_bubble_up(queue
, 0);
510 svn_priority_queue__pop(svn_priority_queue__t
*queue
)
512 if (queue
->elements
->nelts
)
514 memmove(queue
->elements
->elts
,
515 queue
->elements
->elts
516 + (queue
->elements
->nelts
- 1) * queue
->elements
->elt_size
,
517 queue
->elements
->elt_size
);
518 --queue
->elements
->nelts
;
519 heap_bubble_up(queue
, 0);
524 svn_priority_queue__push(svn_priority_queue__t
*queue
,
527 /* we cannot duplicate elements due to potential array re-allocs */
528 assert(element
&& element
!= queue
->elements
->elts
);
530 memcpy(apr_array_push(queue
->elements
), element
, queue
->elements
->elt_size
);
531 heap_bubble_down(queue
, queue
->elements
->nelts
- 1);