Blame stdlib/msort.c

Packit Service 82fcde
/* An alternative to qsort, with an identical interface.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Copyright (C) 1992-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   Written by Mike Haertel, September 1988.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <alloca.h>
Packit Service 82fcde
#include <stdint.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <memcopy.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <atomic.h>
Packit Service 82fcde
Packit Service 82fcde
struct msort_param
Packit Service 82fcde
{
Packit Service 82fcde
  size_t s;
Packit Service 82fcde
  size_t var;
Packit Service 82fcde
  __compar_d_fn_t cmp;
Packit Service 82fcde
  void *arg;
Packit Service 82fcde
  char *t;
Packit Service 82fcde
};
Packit Service 82fcde
static void msort_with_tmp (const struct msort_param *p, void *b, size_t n);
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
msort_with_tmp (const struct msort_param *p, void *b, size_t n)
Packit Service 82fcde
{
Packit Service 82fcde
  char *b1, *b2;
Packit Service 82fcde
  size_t n1, n2;
Packit Service 82fcde
Packit Service 82fcde
  if (n <= 1)
Packit Service 82fcde
    return;
Packit Service 82fcde
Packit Service 82fcde
  n1 = n / 2;
Packit Service 82fcde
  n2 = n - n1;
Packit Service 82fcde
  b1 = b;
Packit Service 82fcde
  b2 = (char *) b + (n1 * p->s);
Packit Service 82fcde
Packit Service 82fcde
  msort_with_tmp (p, b1, n1);
Packit Service 82fcde
  msort_with_tmp (p, b2, n2);
Packit Service 82fcde
Packit Service 82fcde
  char *tmp = p->t;
Packit Service 82fcde
  const size_t s = p->s;
Packit Service 82fcde
  __compar_d_fn_t cmp = p->cmp;
Packit Service 82fcde
  void *arg = p->arg;
Packit Service 82fcde
  switch (p->var)
Packit Service 82fcde
    {
Packit Service 82fcde
    case 0:
Packit Service 82fcde
      while (n1 > 0 && n2 > 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if ((*cmp) (b1, b2, arg) <= 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      *(uint32_t *) tmp = *(uint32_t *) b1;
Packit Service 82fcde
	      b1 += sizeof (uint32_t);
Packit Service 82fcde
	      --n1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      *(uint32_t *) tmp = *(uint32_t *) b2;
Packit Service 82fcde
	      b2 += sizeof (uint32_t);
Packit Service 82fcde
	      --n2;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  tmp += sizeof (uint32_t);
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
    case 1:
Packit Service 82fcde
      while (n1 > 0 && n2 > 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if ((*cmp) (b1, b2, arg) <= 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      *(uint64_t *) tmp = *(uint64_t *) b1;
Packit Service 82fcde
	      b1 += sizeof (uint64_t);
Packit Service 82fcde
	      --n1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      *(uint64_t *) tmp = *(uint64_t *) b2;
Packit Service 82fcde
	      b2 += sizeof (uint64_t);
Packit Service 82fcde
	      --n2;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  tmp += sizeof (uint64_t);
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
    case 2:
Packit Service 82fcde
      while (n1 > 0 && n2 > 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  unsigned long *tmpl = (unsigned long *) tmp;
Packit Service 82fcde
	  unsigned long *bl;
Packit Service 82fcde
Packit Service 82fcde
	  tmp += s;
Packit Service 82fcde
	  if ((*cmp) (b1, b2, arg) <= 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      bl = (unsigned long *) b1;
Packit Service 82fcde
	      b1 += s;
Packit Service 82fcde
	      --n1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      bl = (unsigned long *) b2;
Packit Service 82fcde
	      b2 += s;
Packit Service 82fcde
	      --n2;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  while (tmpl < (unsigned long *) tmp)
Packit Service 82fcde
	    *tmpl++ = *bl++;
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
    case 3:
Packit Service 82fcde
      while (n1 > 0 && n2 > 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if ((*cmp) (*(const void **) b1, *(const void **) b2, arg) <= 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      *(void **) tmp = *(void **) b1;
Packit Service 82fcde
	      b1 += sizeof (void *);
Packit Service 82fcde
	      --n1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      *(void **) tmp = *(void **) b2;
Packit Service 82fcde
	      b2 += sizeof (void *);
Packit Service 82fcde
	      --n2;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  tmp += sizeof (void *);
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
    default:
Packit Service 82fcde
      while (n1 > 0 && n2 > 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if ((*cmp) (b1, b2, arg) <= 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      tmp = (char *) __mempcpy (tmp, b1, s);
Packit Service 82fcde
	      b1 += s;
Packit Service 82fcde
	      --n1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    {
Packit Service 82fcde
	      tmp = (char *) __mempcpy (tmp, b2, s);
Packit Service 82fcde
	      b2 += s;
Packit Service 82fcde
	      --n2;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (n1 > 0)
Packit Service 82fcde
    memcpy (tmp, b1, n1 * s);
Packit Service 82fcde
  memcpy (b, p->t, (n - n2) * s);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
__qsort_r (void *b, size_t n, size_t s, __compar_d_fn_t cmp, void *arg)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t size = n * s;
Packit Service 82fcde
  char *tmp = NULL;
Packit Service 82fcde
  struct msort_param p;
Packit Service 82fcde
Packit Service 82fcde
  /* For large object sizes use indirect sorting.  */
Packit Service 82fcde
  if (s > 32)
Packit Service 82fcde
    size = 2 * n * sizeof (void *) + s;
Packit Service 82fcde
Packit Service 82fcde
  if (size < 1024)
Packit Service 82fcde
    /* The temporary array is small, so put it on the stack.  */
Packit Service 82fcde
    p.t = __alloca (size);
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* We should avoid allocating too much memory since this might
Packit Service 82fcde
	 have to be backed up by swap space.  */
Packit Service 82fcde
      static long int phys_pages;
Packit Service 82fcde
      static int pagesize;
Packit Service 82fcde
Packit Service 82fcde
      if (pagesize == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  phys_pages = __sysconf (_SC_PHYS_PAGES);
Packit Service 82fcde
Packit Service 82fcde
	  if (phys_pages == -1)
Packit Service 82fcde
	    /* Error while determining the memory size.  So let's
Packit Service 82fcde
	       assume there is enough memory.  Otherwise the
Packit Service 82fcde
	       implementer should provide a complete implementation of
Packit Service 82fcde
	       the `sysconf' function.  */
Packit Service 82fcde
	    phys_pages = (long int) (~0ul >> 1);
Packit Service 82fcde
Packit Service 82fcde
	  /* The following determines that we will never use more than
Packit Service 82fcde
	     a quarter of the physical memory.  */
Packit Service 82fcde
	  phys_pages /= 4;
Packit Service 82fcde
Packit Service 82fcde
	  /* Make sure phys_pages is written to memory.  */
Packit Service 82fcde
	  atomic_write_barrier ();
Packit Service 82fcde
Packit Service 82fcde
	  pagesize = __sysconf (_SC_PAGESIZE);
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Just a comment here.  We cannot compute
Packit Service 82fcde
	   phys_pages * pagesize
Packit Service 82fcde
	   and compare the needed amount of memory against this value.
Packit Service 82fcde
	   The problem is that some systems might have more physical
Packit Service 82fcde
	   memory then can be represented with a `size_t' value (when
Packit Service 82fcde
	   measured in bytes.  */
Packit Service 82fcde
Packit Service 82fcde
      /* If the memory requirements are too high don't allocate memory.  */
Packit Service 82fcde
      if (size / pagesize > (size_t) phys_pages)
Packit Service 82fcde
	{
Packit Service 82fcde
	  _quicksort (b, n, s, cmp, arg);
Packit Service 82fcde
	  return;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* It's somewhat large, so malloc it.  */
Packit Service 82fcde
      int save = errno;
Packit Service 82fcde
      tmp = malloc (size);
Packit Service 82fcde
      __set_errno (save);
Packit Service 82fcde
      if (tmp == NULL)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Couldn't get space, so use the slower algorithm
Packit Service 82fcde
	     that doesn't need a temporary array.  */
Packit Service 82fcde
	  _quicksort (b, n, s, cmp, arg);
Packit Service 82fcde
	  return;
Packit Service 82fcde
	}
Packit Service 82fcde
      p.t = tmp;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  p.s = s;
Packit Service 82fcde
  p.var = 4;
Packit Service 82fcde
  p.cmp = cmp;
Packit Service 82fcde
  p.arg = arg;
Packit Service 82fcde
Packit Service 82fcde
  if (s > 32)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Indirect sorting.  */
Packit Service 82fcde
      char *ip = (char *) b;
Packit Service 82fcde
      void **tp = (void **) (p.t + n * sizeof (void *));
Packit Service 82fcde
      void **t = tp;
Packit Service 82fcde
      void *tmp_storage = (void *) (tp + n);
Packit Service 82fcde
Packit Service 82fcde
      while ((void *) t < tmp_storage)
Packit Service 82fcde
	{
Packit Service 82fcde
	  *t++ = ip;
Packit Service 82fcde
	  ip += s;
Packit Service 82fcde
	}
Packit Service 82fcde
      p.s = sizeof (void *);
Packit Service 82fcde
      p.var = 3;
Packit Service 82fcde
      msort_with_tmp (&p, p.t + n * sizeof (void *), n);
Packit Service 82fcde
Packit Service 82fcde
      /* tp[0] .. tp[n - 1] is now sorted, copy around entries of
Packit Service 82fcde
	 the original array.  Knuth vol. 3 (2nd ed.) exercise 5.2-10.  */
Packit Service 82fcde
      char *kp;
Packit Service 82fcde
      size_t i;
Packit Service 82fcde
      for (i = 0, ip = (char *) b; i < n; i++, ip += s)
Packit Service 82fcde
	if ((kp = tp[i]) != ip)
Packit Service 82fcde
	  {
Packit Service 82fcde
	    size_t j = i;
Packit Service 82fcde
	    char *jp = ip;
Packit Service 82fcde
	    memcpy (tmp_storage, ip, s);
Packit Service 82fcde
Packit Service 82fcde
	    do
Packit Service 82fcde
	      {
Packit Service 82fcde
		size_t k = (kp - (char *) b) / s;
Packit Service 82fcde
		tp[j] = jp;
Packit Service 82fcde
		memcpy (jp, kp, s);
Packit Service 82fcde
		j = k;
Packit Service 82fcde
		jp = kp;
Packit Service 82fcde
		kp = tp[k];
Packit Service 82fcde
	      }
Packit Service 82fcde
	    while (kp != ip);
Packit Service 82fcde
Packit Service 82fcde
	    tp[j] = jp;
Packit Service 82fcde
	    memcpy (jp, tmp_storage, s);
Packit Service 82fcde
	  }
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      if ((s & (sizeof (uint32_t) - 1)) == 0
Packit Service 82fcde
	  && ((char *) b - (char *) 0) % __alignof__ (uint32_t) == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (s == sizeof (uint32_t))
Packit Service 82fcde
	    p.var = 0;
Packit Service 82fcde
	  else if (s == sizeof (uint64_t)
Packit Service 82fcde
		   && ((char *) b - (char *) 0) % __alignof__ (uint64_t) == 0)
Packit Service 82fcde
	    p.var = 1;
Packit Service 82fcde
	  else if ((s & (sizeof (unsigned long) - 1)) == 0
Packit Service 82fcde
		   && ((char *) b - (char *) 0)
Packit Service 82fcde
		      % __alignof__ (unsigned long) == 0)
Packit Service 82fcde
	    p.var = 2;
Packit Service 82fcde
	}
Packit Service 82fcde
      msort_with_tmp (&p, b, n);
Packit Service 82fcde
    }
Packit Service 82fcde
  free (tmp);
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (__qsort_r)
Packit Service 82fcde
weak_alias (__qsort_r, qsort_r)
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
qsort (void *b, size_t n, size_t s, __compar_fn_t cmp)
Packit Service 82fcde
{
Packit Service 82fcde
  return __qsort_r (b, n, s, (__compar_d_fn_t) cmp, NULL);
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (qsort)