Blame stdlib/msort.c

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