sorts.c revision 299742
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
38
39/*** svn_sort__hash() ***/
40
41/* (Should this be a permanent part of APR?)
42
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.
46
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.
50
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.
56
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!
63 */
64
65
66int
67svn_sort_compare_items_as_paths(const svn_sort__item_t *a,
68                                const svn_sort__item_t *b)
69{
70  const char *astr, *bstr;
71
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);
77}
78
79
80int
81svn_sort_compare_items_lexically(const svn_sort__item_t *a,
82                                 const svn_sort__item_t *b)
83{
84  int val;
85  apr_size_t len;
86
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;
92
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;
95}
96
97
98int
99svn_sort_compare_revisions(const void *a, const void *b)
100{
101  svn_revnum_t a_rev = *(const svn_revnum_t *)a;
102  svn_revnum_t b_rev = *(const svn_revnum_t *)b;
103
104  if (a_rev == b_rev)
105    return 0;
106
107  return a_rev < b_rev ? 1 : -1;
108}
109
110
111int
112svn_sort_compare_paths(const void *a, const void *b)
113{
114  const char *item1 = *((const char * const *) a);
115  const char *item2 = *((const char * const *) b);
116
117  return svn_path_compare_paths(item1, item2);
118}
119
120
121int
122svn_sort_compare_ranges(const void *a, const void *b)
123{
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);
126
127  if (item1->start == item2->start
128      && item1->end == item2->end)
129    return 0;
130
131  if (item1->start == item2->start)
132    return item1->end < item2->end ? -1 : 1;
133
134  return item1->start < item2->start ? -1 : 1;
135}
136
137void
138svn_sort__array(apr_array_header_t *array,
139                int (*comparison_func)(const void *,
140                                       const void *))
141{
142  qsort(array->elts, array->nelts, array->elt_size, comparison_func);
143}
144
145apr_array_header_t *
146svn_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)
150{
151  apr_hash_index_t *hi;
152  apr_array_header_t *ary;
153  svn_boolean_t sorted;
154  svn_sort__item_t *prev_item;
155
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));
158
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))
163    {
164      svn_sort__item_t *item = apr_array_push(ary);
165
166      apr_hash_this(hi, &item->key, &item->klen, &item->value);
167
168      if (prev_item == NULL)
169        {
170          prev_item = item;
171          continue;
172        }
173
174      if (sorted)
175        {
176          sorted = (comparison_func(prev_item, item) < 0);
177          prev_item = item;
178        }
179    }
180
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);
185
186  return ary;
187}
188
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.
197 */
198static int
199bsearch_lower_bound(const void *key,
200                    const void *base,
201                    int nelts,
202                    int elt_size,
203                    int (*compare_func)(const void *, const void *))
204{
205  int lower = 0;
206  int upper = nelts - 1;
207
208  /* Binary search for the lowest position at which to insert KEY. */
209  while (lower <= upper)
210    {
211      int try = lower + (upper - lower) / 2;  /* careful to avoid overflow */
212      int cmp = compare_func((const char *)base + try * elt_size, key);
213
214      if (cmp < 0)
215        lower = try + 1;
216      else
217        upper = try - 1;
218    }
219  assert(lower == upper + 1);
220
221  return lower;
222}
223
224int
225svn_sort__bsearch_lower_bound(const apr_array_header_t *array,
226                              const void *key,
227                              int (*compare_func)(const void *, const void *))
228{
229  return bsearch_lower_bound(key,
230                             array->elts, array->nelts, array->elt_size,
231                             compare_func);
232}
233
234void *
235svn_sort__array_lookup(const apr_array_header_t *array,
236                       const void *key,
237                       int *hint,
238                       int (*compare_func)(const void *, const void *))
239{
240  void *result;
241  int idx;
242
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)
246    {
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)
251        {
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;
257
258          *hint = array->nelts;
259          if (array->nelts == 0)
260            return NULL;
261
262          offset = (array->nelts - 1) * array->elt_size;
263          if (compare_func(array->elts + offset, key) < 0)
264            return NULL;
265        }
266      else if (idx > 0)
267        {
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;
272
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;
278        }
279      else if (idx <= 0)
280        {
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;
286        }
287
288      /* The HINT did not help. */
289    }
290
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;
297
298  result = array->elts + idx * array->elt_size;
299  return compare_func(result, key) ? NULL : result;
300}
301
302void
303svn_sort__array_insert(apr_array_header_t *array,
304                       const void *new_element,
305                       int insert_index)
306{
307  int elements_to_move;
308  char *new_position;
309
310  assert(0 <= insert_index && insert_index <= array->nelts);
311  elements_to_move = array->nelts - insert_index;  /* before bumping nelts */
312
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);
316
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);
322
323  /* Copy in the new element */
324  memcpy(new_position, new_element, array->elt_size);
325}
326
327void
328svn_sort__array_delete(apr_array_header_t *arr,
329                       int delete_index,
330                       int elements_to_delete)
331{
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      && (elements_to_delete + delete_index) <= arr->nelts)
337    {
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));
346
347      /* Delete the last ELEMENTS_TO_DELETE elements. */
348      arr->nelts -= elements_to_delete;
349    }
350}
351
352void
353svn_sort__array_reverse(apr_array_header_t *array,
354                        apr_pool_t *scratch_pool)
355{
356  int i;
357
358  if (array->elt_size == sizeof(void *))
359    {
360      for (i = 0; i < array->nelts / 2; i++)
361        {
362          int swap_index = array->nelts - i - 1;
363          void *tmp = APR_ARRAY_IDX(array, i, void *);
364
365          APR_ARRAY_IDX(array, i, void *) =
366            APR_ARRAY_IDX(array, swap_index, void *);
367          APR_ARRAY_IDX(array, swap_index, void *) = tmp;
368        }
369    }
370  else
371    {
372      apr_size_t sz = array->elt_size;
373      char *tmp = apr_palloc(scratch_pool, sz);
374
375      for (i = 0; i < array->nelts / 2; i++)
376        {
377          int swap_index = array->nelts - i - 1;
378          char *x = array->elts + (sz * i);
379          char *y = array->elts + (sz * swap_index);
380
381          memcpy(tmp, x, sz);
382          memcpy(x, y, sz);
383          memcpy(y, tmp, sz);
384        }
385    }
386}
387
388/* Our priority queue data structure:
389 * Simply remember the constructor parameters.
390 */
391struct svn_priority_queue__t
392{
393  /* the queue elements, ordered as a heap according to COMPARE_FUNC */
394  apr_array_header_t *elements;
395
396  /* predicate used to order the heap */
397  int (*compare_func)(const void *, const void *);
398};
399
400/* Return TRUE, if heap element number LHS in QUEUE is smaller than element
401 * number RHS according to QUEUE->COMPARE_FUNC
402 */
403static int
404heap_is_less(svn_priority_queue__t *queue,
405             apr_size_t lhs,
406             apr_size_t rhs)
407{
408  char *lhs_value = queue->elements->elts + lhs * queue->elements->elt_size;
409  char *rhs_value = queue->elements->elts + rhs * queue->elements->elt_size;
410
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;
415}
416
417/* Exchange elements number LHS and RHS in QUEUE.
418 */
419static void
420heap_swap(svn_priority_queue__t *queue,
421          apr_size_t lhs,
422          apr_size_t rhs)
423{
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;
427
428  for (i = 0; i < queue->elements->elt_size; ++i)
429    {
430      char temp = lhs_value[i];
431      lhs_value[i] = rhs_value[i];
432      rhs_value[i] = temp;
433    }
434}
435
436/* Move element number IDX to lower indexes until the heap criterion is
437 * fulfilled again.
438 */
439static void
440heap_bubble_down(svn_priority_queue__t *queue,
441                 int idx)
442{
443  while (idx > 0 && heap_is_less(queue, idx, (idx - 1) / 2))
444    {
445      heap_swap(queue, idx, (idx - 1) / 2);
446      idx = (idx - 1) / 2;
447    }
448}
449
450/* Move element number IDX to higher indexes until the heap criterion is
451 * fulfilled again.
452 */
453static void
454heap_bubble_up(svn_priority_queue__t *queue,
455               int idx)
456{
457  while (2 * idx + 2 < queue->elements->nelts)
458    {
459      int child = heap_is_less(queue, 2 * idx + 1, 2 * idx + 2)
460                ? 2 * idx + 1
461                : 2 * idx + 2;
462
463      if (heap_is_less(queue, idx, child))
464        return;
465
466      heap_swap(queue, idx, child);
467      idx = child;
468    }
469
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);
473}
474
475svn_priority_queue__t *
476svn_priority_queue__create(apr_array_header_t *elements,
477                           int (*compare_func)(const void *, const void *))
478{
479  int i;
480
481  svn_priority_queue__t *queue = apr_pcalloc(elements->pool, sizeof(*queue));
482  queue->elements = elements;
483  queue->compare_func = compare_func;
484
485  for (i = elements->nelts / 2; i >= 0; --i)
486    heap_bubble_up(queue, i);
487
488  return queue;
489}
490
491apr_size_t
492svn_priority_queue__size(svn_priority_queue__t *queue)
493{
494  return queue->elements->nelts;
495}
496
497void *
498svn_priority_queue__peek(svn_priority_queue__t *queue)
499{
500  return queue->elements->nelts ? queue->elements->elts : NULL;
501}
502
503void
504svn_priority_queue__update(svn_priority_queue__t *queue)
505{
506  heap_bubble_up(queue, 0);
507}
508
509void
510svn_priority_queue__pop(svn_priority_queue__t *queue)
511{
512  if (queue->elements->nelts)
513    {
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);
520    }
521}
522
523void
524svn_priority_queue__push(svn_priority_queue__t *queue,
525                         const void *element)
526{
527  /* we cannot duplicate elements due to potential array re-allocs */
528  assert(element && element != queue->elements->elts);
529
530  memcpy(apr_array_push(queue->elements), element, queue->elements->elt_size);
531  heap_bubble_down(queue, queue->elements->nelts - 1);
532}
533