/****************************************************************************
 * libc/stdlib/lib_bsearch.c
 *
 *   Copyright (c) 1990, 1993
 *   The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <stdlib.h>
#include <assert.h>

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: bsearch
 *
 * Description:
 *   The bsearch() function will search an array of nel objects, the initial
 *   element of which is pointed to by 'base', for an element that matches
 *   the object pointed to by 'key'. The size of each element in the array
 *   is specified by 'width'. If the nel argument has the value zero, the
 *   comparison function pointed to by 'compar' will not be called and no
 *   match will be found.
 *
 *   The comparison function pointed to by 'compar' will be called with two
 *   arguments that point to the 'key' object and to an array element, in
 *   that order.
 *
 *   The application will ensure that the comparison function pointed to by
 *   'compar 'does not alter the contents of the array. The implementation
 *   may reorder elements of the array between calls to the comparison
 *   function, but will not alter the contents of any individual element.
 *
 *   The implementation will ensure that the first argument is always a
 *   pointer to the 'key'.
 *
 *   When the same objects (consisting of width bytes, irrespective of their
 *   current positions in the array) are passed more than once to the
 *   comparison function, the results will be consistent with one another.
 *   That is, the same object will always compare the same way with the key.
 *
 *   The application will ensure that the function returns an integer less
 *   than, equal to, or greater than 0 if the key object is considered,
 *   respectively, to be less than, to match, or to be greater than the
 *   array element. The application will ensure that the array consists of
 *   all the elements that compare less than, all the elements that compare
 *   equal to, and all the elements that compare greater than the key
 *   object, in that order.
 *
 *   (Based on description from OpenGroup.org).
 *
 * Returned Value:
 *   The bsearch() function will return a pointer to a matching member of
 *   the array, or a null pointer if no match is found. If two or more
 *   members compare equal, which member is returned is unspecified.
 *
 * Notes from the NetBSD version:
 *   The code below is a bit sneaky.  After a comparison fails, we divide
 *   the work in half by moving either left or right. If 'lim' is odd,
 *   moving left simply involves halving 'lim': e.g., when 'lim' is 5 we
 *   look at item 2, so we change 'lim' to 2 so that we will look at items
 *   0 & 1.  If 'lim' is even, the same applies.  If 'lim' is odd, moving
 *   right again involes halving 'lim', this time moving the base up one
 *   item past 'middle': e.g., when 'lim' is 5 we change base to item 3 and
 *   make 'lim' 2 so that we will look at items 3 and 4.  If 'lim' is
 *   even, however, we have to shrink it by one before halving: e.g.,
 *   when 'lim' is 4, we still looked at item 2, so we have to make 'lim'
 *   3, then halve, obtaining 1, so that we will only look at item 3.
 *
 ****************************************************************************/

FAR void *bsearch(FAR const void *key, FAR const void *base, size_t nel,
                  size_t width, CODE int (*compar)(FAR const void *,
                  FAR const void *))
{
  FAR const void *middle;  /* Current entry being tested */
  FAR const char *lower;   /* The lower limit of the search region */
  size_t lim;              /* The number of elements in the region */
  int cmp;                 /* Boolean comparison result */

  DEBUGASSERT(key != NULL);
  DEBUGASSERT(base != NULL || nel == 0);
  DEBUGASSERT(compar != NULL);

  for (lim = nel, lower = (const char *)base; lim != 0; lim >>= 1)
    {
      middle = lower + (lim >> 1) * width;
      cmp    = (*compar)(key, middle);

      if (cmp == 0)
        {
          return (FAR void *)middle;
        }

      if (cmp > 0)
        {
          /* key > middle: move right (else move left) */

          lower = (FAR const char *)middle + width;
          lim--;
        }
    }

  return NULL;
}
