Win32: fix an incorrect error status being propagated to the caller in case
[svn/apache.git] / subversion / libsvn_subr / sorts.c
blob613c93b288a4fb4be8bd76f26d16f3fcf85b271f
1 /*
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
20 * under the License.
21 * ====================================================================
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"
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
49 one.
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!
66 int
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;
72 astr = a->key;
73 bstr = b->key;
74 assert(astr[a->klen] == '\0');
75 assert(bstr[b->klen] == '\0');
76 return svn_path_compare_paths(astr, bstr);
80 int
81 svn_sort_compare_items_lexically(const svn_sort__item_t *a,
82 const svn_sort__item_t *b)
84 int val;
85 apr_size_t len;
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);
90 if (val != 0)
91 return val;
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;
98 int
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;
104 if (a_rev == b_rev)
105 return 0;
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)
129 return 0;
131 if (item1->start == item2->start)
132 return item1->end < item2->end ? -1 : 1;
134 return item1->start < item2->start ? -1 : 1;
137 void
138 svn_sort__array(apr_array_header_t *array,
139 int (*comparison_func)(const void *,
140 const void *))
142 qsort(array->elts, array->nelts, array->elt_size, comparison_func);
145 apr_array_header_t *
146 svn_sort__hash(apr_hash_t *ht,
147 int (*comparison_func)(const svn_sort__item_t *,
148 const svn_sort__item_t *),
149 apr_pool_t *pool)
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 */
160 sorted = TRUE;
161 prev_item = NULL;
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)
170 prev_item = item;
171 continue;
174 if (sorted)
176 sorted = (comparison_func(prev_item, item) < 0);
177 prev_item = item;
181 /* quicksort the array if it isn't already sorted. */
182 if (!sorted)
183 svn_sort__array(ary,
184 (int (*)(const void *, const void *))comparison_func);
186 return ary;
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
196 C++ STL.
198 static int
199 bsearch_lower_bound(const void *key,
200 const void *base,
201 int nelts,
202 int elt_size,
203 int (*compare_func)(const void *, const void *))
205 int lower = 0;
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);
214 if (cmp < 0)
215 lower = try + 1;
216 else
217 upper = try - 1;
219 assert(lower == upper + 1);
221 return lower;
225 svn_sort__bsearch_lower_bound(const apr_array_header_t *array,
226 const void *key,
227 int (*compare_func)(const void *, const void *))
229 return bsearch_lower_bound(key,
230 array->elts, array->nelts, array->elt_size,
231 compare_func);
234 void *
235 svn_sort__array_lookup(const apr_array_header_t *array,
236 const void *key,
237 int *hint,
238 int (*compare_func)(const void *, const void *))
240 void *result;
241 int idx;
243 /* If provided, try the index following *HINT (i.e. probably the last
244 * hit location) first. This speeds up linear scans. */
245 if (hint)
247 /* We intend to insert right behind *HINT.
248 * Exit this function early, if we actually can. */
249 idx = *hint + 1;
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
255 * return NULL. */
256 apr_size_t offset;
258 *hint = array->nelts;
259 if (array->nelts == 0)
260 return NULL;
262 offset = (array->nelts - 1) * array->elt_size;
263 if (compare_func(array->elts + offset, key) < 0)
264 return NULL;
266 else if (idx > 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. */
271 void *previous;
273 *hint = idx;
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))
277 return result;
279 else if (idx <= 0)
281 /* Intend to insert at the beginning of an non-empty array.
282 * That requires the first entry to be larger than KEY. */
283 *hint = 0;
284 if (!compare_func(array->elts, key))
285 return array->elts;
288 /* The HINT did not help. */
291 idx = bsearch_lower_bound(key, array->elts, array->nelts, array->elt_size,
292 compare_func);
293 if (hint)
294 *hint = idx;
295 if (idx >= array->nelts)
296 return NULL;
298 result = array->elts + idx * array->elt_size;
299 return compare_func(result, key) ? NULL : result;
302 void
303 svn_sort__array_insert(apr_array_header_t *array,
304 const void *new_element,
305 int insert_index)
307 int elements_to_move;
308 char *new_position;
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,
318 this is a no-op.) */
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);
327 void
328 svn_sort__array_delete(apr_array_header_t *arr,
329 int delete_index,
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)
342 memmove(
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;
352 void
353 svn_sort__array_reverse(apr_array_header_t *array,
354 apr_pool_t *scratch_pool)
356 int i;
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;
370 else
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);
381 memcpy(tmp, x, sz);
382 memcpy(x, y, sz);
383 memcpy(y, tmp, sz);
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
403 static int
404 heap_is_less(svn_priority_queue__t *queue,
405 apr_size_t lhs,
406 apr_size_t rhs)
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.
419 static void
420 heap_swap(svn_priority_queue__t *queue,
421 apr_size_t lhs,
422 apr_size_t rhs)
424 int i;
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];
432 rhs_value[i] = temp;
436 /* Move element number IDX to lower indexes until the heap criterion is
437 * fulfilled again.
439 static void
440 heap_bubble_down(svn_priority_queue__t *queue,
441 int idx)
443 while (idx > 0 && heap_is_less(queue, idx, (idx - 1) / 2))
445 heap_swap(queue, idx, (idx - 1) / 2);
446 idx = (idx - 1) / 2;
450 /* Move element number IDX to higher indexes until the heap criterion is
451 * fulfilled again.
453 static void
454 heap_bubble_up(svn_priority_queue__t *queue,
455 int idx)
457 while (2 * idx + 2 < queue->elements->nelts)
459 int child = heap_is_less(queue, 2 * idx + 1, 2 * idx + 2)
460 ? 2 * idx + 1
461 : 2 * idx + 2;
463 if (heap_is_less(queue, idx, child))
464 return;
466 heap_swap(queue, idx, child);
467 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 *))
479 int i;
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);
488 return queue;
491 apr_size_t
492 svn_priority_queue__size(svn_priority_queue__t *queue)
494 return queue->elements->nelts;
497 void *
498 svn_priority_queue__peek(svn_priority_queue__t *queue)
500 return queue->elements->nelts ? queue->elements->elts : NULL;
503 void
504 svn_priority_queue__update(svn_priority_queue__t *queue)
506 heap_bubble_up(queue, 0);
509 void
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);
523 void
524 svn_priority_queue__push(svn_priority_queue__t *queue,
525 const void *element)
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);