1251881Speter/*
2251881Speter * sorts.c:   all sorts of sorts
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <apr_pools.h>
27251881Speter#include <apr_hash.h>
28251881Speter#include <apr_tables.h>
29251881Speter#include <stdlib.h>       /* for qsort()   */
30251881Speter#include <assert.h>
31251881Speter#include "svn_hash.h"
32251881Speter#include "svn_path.h"
33251881Speter#include "svn_sorts.h"
34251881Speter#include "svn_error.h"
35251881Speter
36251881Speter
37251881Speter
38251881Speter/*** svn_sort__hash() ***/
39251881Speter
40251881Speter/* (Should this be a permanent part of APR?)
41251881Speter
42251881Speter   OK, folks, here's what's going on.  APR hash tables hash on
43251881Speter   key/klen objects, and store associated generic values.  They work
44251881Speter   great, but they have no ordering.
45251881Speter
46251881Speter   The point of this exercise is to somehow arrange a hash's keys into
47251881Speter   an "ordered list" of some kind -- in this case, a nicely sorted
48251881Speter   one.
49251881Speter
50251881Speter   We're using APR arrays, therefore, because that's what they are:
51251881Speter   ordered lists.  However, what "keys" should we put in the array?
52251881Speter   Clearly, (const char *) objects aren't general enough.  Or rather,
53251881Speter   they're not as general as APR's hash implementation, which stores
54251881Speter   (void *)/length as keys.  We don't want to lose this information.
55251881Speter
56251881Speter   Therefore, it makes sense to store pointers to {void *, size_t}
57251881Speter   structures in our array.  No such apr object exists... BUT... if we
58251881Speter   can use a new type svn_sort__item_t which contains {char *, size_t, void
59251881Speter   *}.  If store these objects in our array, we get the hash value
60251881Speter   *for free*.  When looping over the final array, we don't need to
61251881Speter   call apr_hash_get().  Major bonus!
62251881Speter */
63251881Speter
64251881Speter
65251881Speterint
66251881Spetersvn_sort_compare_items_as_paths(const svn_sort__item_t *a,
67251881Speter                                const svn_sort__item_t *b)
68251881Speter{
69251881Speter  const char *astr, *bstr;
70251881Speter
71251881Speter  astr = a->key;
72251881Speter  bstr = b->key;
73251881Speter  assert(astr[a->klen] == '\0');
74251881Speter  assert(bstr[b->klen] == '\0');
75251881Speter  return svn_path_compare_paths(astr, bstr);
76251881Speter}
77251881Speter
78251881Speter
79251881Speterint
80251881Spetersvn_sort_compare_items_lexically(const svn_sort__item_t *a,
81251881Speter                                 const svn_sort__item_t *b)
82251881Speter{
83251881Speter  int val;
84251881Speter  apr_size_t len;
85251881Speter
86251881Speter  /* Compare bytes of a's key and b's key up to the common length. */
87251881Speter  len = (a->klen < b->klen) ? a->klen : b->klen;
88251881Speter  val = memcmp(a->key, b->key, len);
89251881Speter  if (val != 0)
90251881Speter    return val;
91251881Speter
92251881Speter  /* They match up until one of them ends; whichever is longer is greater. */
93251881Speter  return (a->klen < b->klen) ? -1 : (a->klen > b->klen) ? 1 : 0;
94251881Speter}
95251881Speter
96251881Speter
97251881Speterint
98251881Spetersvn_sort_compare_revisions(const void *a, const void *b)
99251881Speter{
100251881Speter  svn_revnum_t a_rev = *(const svn_revnum_t *)a;
101251881Speter  svn_revnum_t b_rev = *(const svn_revnum_t *)b;
102251881Speter
103251881Speter  if (a_rev == b_rev)
104251881Speter    return 0;
105251881Speter
106251881Speter  return a_rev < b_rev ? 1 : -1;
107251881Speter}
108251881Speter
109251881Speter
110251881Speterint
111251881Spetersvn_sort_compare_paths(const void *a, const void *b)
112251881Speter{
113251881Speter  const char *item1 = *((const char * const *) a);
114251881Speter  const char *item2 = *((const char * const *) b);
115251881Speter
116251881Speter  return svn_path_compare_paths(item1, item2);
117251881Speter}
118251881Speter
119251881Speter
120251881Speterint
121251881Spetersvn_sort_compare_ranges(const void *a, const void *b)
122251881Speter{
123251881Speter  const svn_merge_range_t *item1 = *((const svn_merge_range_t * const *) a);
124251881Speter  const svn_merge_range_t *item2 = *((const svn_merge_range_t * const *) b);
125251881Speter
126251881Speter  if (item1->start == item2->start
127251881Speter      && item1->end == item2->end)
128251881Speter    return 0;
129251881Speter
130251881Speter  if (item1->start == item2->start)
131251881Speter    return item1->end < item2->end ? -1 : 1;
132251881Speter
133251881Speter  return item1->start < item2->start ? -1 : 1;
134251881Speter}
135251881Speter
136251881Speterapr_array_header_t *
137251881Spetersvn_sort__hash(apr_hash_t *ht,
138251881Speter               int (*comparison_func)(const svn_sort__item_t *,
139251881Speter                                      const svn_sort__item_t *),
140251881Speter               apr_pool_t *pool)
141251881Speter{
142251881Speter  apr_hash_index_t *hi;
143251881Speter  apr_array_header_t *ary;
144251881Speter  svn_boolean_t sorted;
145251881Speter  svn_sort__item_t *prev_item;
146251881Speter
147251881Speter  /* allocate an array with enough elements to hold all the keys. */
148251881Speter  ary = apr_array_make(pool, apr_hash_count(ht), sizeof(svn_sort__item_t));
149251881Speter
150251881Speter  /* loop over hash table and push all keys into the array */
151251881Speter  sorted = TRUE;
152251881Speter  prev_item = NULL;
153251881Speter  for (hi = apr_hash_first(pool, ht); hi; hi = apr_hash_next(hi))
154251881Speter    {
155251881Speter      svn_sort__item_t *item = apr_array_push(ary);
156251881Speter
157251881Speter      apr_hash_this(hi, &item->key, &item->klen, &item->value);
158251881Speter
159251881Speter      if (prev_item == NULL)
160251881Speter        {
161251881Speter          prev_item = item;
162251881Speter          continue;
163251881Speter        }
164251881Speter
165251881Speter      if (sorted)
166251881Speter        {
167251881Speter          sorted = (comparison_func(prev_item, item) < 0);
168251881Speter          prev_item = item;
169251881Speter        }
170251881Speter    }
171251881Speter
172251881Speter  /* quicksort the array if it isn't already sorted.  */
173251881Speter  if (!sorted)
174251881Speter    qsort(ary->elts, ary->nelts, ary->elt_size,
175251881Speter          (int (*)(const void *, const void *))comparison_func);
176251881Speter
177251881Speter  return ary;
178251881Speter}
179251881Speter
180251881Speter/* Return the lowest index at which the element *KEY should be inserted into
181251881Speter   the array at BASE which has NELTS elements of size ELT_SIZE bytes each,
182251881Speter   according to the ordering defined by COMPARE_FUNC.
183251881Speter   0 <= NELTS <= INT_MAX, 1 <= ELT_SIZE <= INT_MAX.
184251881Speter   The array must already be sorted in the ordering defined by COMPARE_FUNC.
185251881Speter   COMPARE_FUNC is defined as for the C stdlib function bsearch().
186251881Speter   Note: This function is modeled on bsearch() and on lower_bound() in the
187251881Speter   C++ STL.
188251881Speter */
189251881Speterstatic int
190251881Speterbsearch_lower_bound(const void *key,
191251881Speter                    const void *base,
192251881Speter                    int nelts,
193251881Speter                    int elt_size,
194251881Speter                    int (*compare_func)(const void *, const void *))
195251881Speter{
196251881Speter  int lower = 0;
197251881Speter  int upper = nelts - 1;
198251881Speter
199251881Speter  /* Binary search for the lowest position at which to insert KEY. */
200251881Speter  while (lower <= upper)
201251881Speter    {
202251881Speter      int try = lower + (upper - lower) / 2;  /* careful to avoid overflow */
203251881Speter      int cmp = compare_func((const char *)base + try * elt_size, key);
204251881Speter
205251881Speter      if (cmp < 0)
206251881Speter        lower = try + 1;
207251881Speter      else
208251881Speter        upper = try - 1;
209251881Speter    }
210251881Speter  assert(lower == upper + 1);
211251881Speter
212251881Speter  return lower;
213251881Speter}
214251881Speter
215251881Speterint
216251881Spetersvn_sort__bsearch_lower_bound(const void *key,
217251881Speter                              const apr_array_header_t *array,
218251881Speter                              int (*compare_func)(const void *, const void *))
219251881Speter{
220251881Speter  return bsearch_lower_bound(key,
221251881Speter                             array->elts, array->nelts, array->elt_size,
222251881Speter                             compare_func);
223251881Speter}
224251881Speter
225251881Spetervoid
226251881Spetersvn_sort__array_insert(const void *new_element,
227251881Speter                       apr_array_header_t *array,
228251881Speter                       int insert_index)
229251881Speter{
230251881Speter  int elements_to_move;
231251881Speter  char *new_position;
232251881Speter
233251881Speter  assert(0 <= insert_index && insert_index <= array->nelts);
234251881Speter  elements_to_move = array->nelts - insert_index;  /* before bumping nelts */
235251881Speter
236251881Speter  /* Grow the array, allocating a new space at the end. Note: this can
237251881Speter     reallocate the array's "elts" at a different address. */
238251881Speter  apr_array_push(array);
239251881Speter
240251881Speter  /* Move the elements after INSERT_INDEX along. (When elements_to_move == 0,
241251881Speter     this is a no-op.) */
242251881Speter  new_position = (char *)array->elts + insert_index * array->elt_size;
243251881Speter  memmove(new_position + array->elt_size, new_position,
244251881Speter          array->elt_size * elements_to_move);
245251881Speter
246251881Speter  /* Copy in the new element */
247251881Speter  memcpy(new_position, new_element, array->elt_size);
248251881Speter}
249251881Speter
250251881Spetervoid
251251881Spetersvn_sort__array_delete(apr_array_header_t *arr,
252251881Speter                       int delete_index,
253251881Speter                       int elements_to_delete)
254251881Speter{
255251881Speter  /* Do we have a valid index and are there enough elements? */
256251881Speter  if (delete_index >= 0
257251881Speter      && delete_index < arr->nelts
258251881Speter      && elements_to_delete > 0
259251881Speter      && (elements_to_delete + delete_index) <= arr->nelts)
260251881Speter    {
261251881Speter      /* If we are not deleting a block of elements that extends to the end
262251881Speter         of the array, then we need to move the remaining elements to keep
263251881Speter         the array contiguous. */
264251881Speter      if ((elements_to_delete + delete_index) < arr->nelts)
265251881Speter        memmove(
266251881Speter          arr->elts + arr->elt_size * delete_index,
267251881Speter          arr->elts + (arr->elt_size * (delete_index + elements_to_delete)),
268251881Speter          arr->elt_size * (arr->nelts - elements_to_delete - delete_index));
269251881Speter
270251881Speter      /* Delete the last ELEMENTS_TO_DELETE elements. */
271251881Speter      arr->nelts -= elements_to_delete;
272251881Speter    }
273251881Speter}
274251881Speter
275251881Spetervoid
276251881Spetersvn_sort__array_reverse(apr_array_header_t *array,
277251881Speter                        apr_pool_t *scratch_pool)
278251881Speter{
279251881Speter  int i;
280251881Speter
281251881Speter  if (array->elt_size == sizeof(void *))
282251881Speter    {
283251881Speter      for (i = 0; i < array->nelts / 2; i++)
284251881Speter        {
285251881Speter          int swap_index = array->nelts - i - 1;
286251881Speter          void *tmp = APR_ARRAY_IDX(array, i, void *);
287251881Speter
288251881Speter          APR_ARRAY_IDX(array, i, void *) =
289251881Speter            APR_ARRAY_IDX(array, swap_index, void *);
290251881Speter          APR_ARRAY_IDX(array, swap_index, void *) = tmp;
291251881Speter        }
292251881Speter    }
293251881Speter  else
294251881Speter    {
295251881Speter      apr_size_t sz = array->elt_size;
296251881Speter      char *tmp = apr_palloc(scratch_pool, sz);
297251881Speter
298251881Speter      for (i = 0; i < array->nelts / 2; i++)
299251881Speter        {
300251881Speter          int swap_index = array->nelts - i - 1;
301251881Speter          char *x = array->elts + (sz * i);
302251881Speter          char *y = array->elts + (sz * swap_index);
303251881Speter
304251881Speter          memcpy(tmp, x, sz);
305251881Speter          memcpy(x, y, sz);
306251881Speter          memcpy(y, tmp, sz);
307251881Speter        }
308251881Speter    }
309251881Speter}
310