xref: /freebsd-13-stable/contrib/subversion/subversion/libsvn_subr/sorts.c (revision b7ec5dea64b6513b41316a38cc72efa9139bc4ae)
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