bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

new module 'astrxfrm'


From: Bruno Haible
Subject: new module 'astrxfrm'
Date: Sun, 15 Aug 2010 00:25:57 +0200
User-agent: KMail/1.9.9

> >   extern char * astrxfrm (const char *s, char *resultbuf, size_t *lengthp);
> 
> Yes, that looks like a useful addition.  Thanks for the suggestion.


I'm implementing it as follows. Hope the logic is all right. (It's more
complicated than one would expect.)


2010-08-14  Bruno Haible  <address@hidden>

        New module 'astrxfrm'.
        * lib/astrxfrm.h: New file.
        * lib/astrxfrm.c: New file, based on lib/memxfrm.c.
        * modules/astrxfrm: New file.

================================ lib/astrxfrm.h ================================
/* Locale dependent string transformation for comparison.
   Copyright (C) 2010 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef ASTRXFRM_H
#define ASTRXFRM_H

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif


/* Variant of strxfrm() with a calling convention that reduces the number
   of strxfrm calls.  */

/* Transform the string starting at S to a string, in such a way that
   comparing S1 and S2 with strcoll() is equivalent to comparing astrxfrm(S1)
   and astrxfrm(S2) with strcmp().
   The result of this function depends on the LC_COLLATE category of the
   current locale.
   If successful: If resultbuf is not NULL and the result fits into *lengthp
   bytes, it is put in resultbuf, and resultbuf is returned.  Otherwise, a
   freshly allocated string is returned.  In both cases, *lengthp is set to the
   length of the returned string.
   Upon failure, return NULL, with errno set.  */
extern char * astrxfrm (const char *s, char *resultbuf, size_t *lengthp);


#ifdef __cplusplus
}
#endif

#endif /* ASTRXFRM_H */
================================ lib/astrxfrm.c ================================
/* Locale dependent string transformation for comparison.
   Copyright (C) 2010 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 2010.

   This program is free software: you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as published
   by the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include <config.h>

/* Specification.  */
#include "astrxfrm.h"

#include <errno.h>
#include <stdlib.h>
#include <string.h>

char *
astrxfrm (const char *s, char *resultbuf, size_t *lengthp)
{
  char tmpbuf[4000];
  char *result;      /* either == resultbuf or == tmpbuf or freshly allocated
                        or NULL.  */
  size_t allocated;  /* number of bytes allocated at result */
  size_t length;

  if (resultbuf != NULL)
    {
      result = resultbuf;
      allocated = *lengthp;
    }
  else
    {
      result = NULL;
      allocated = 0;
    }

  {
    size_t l = strlen (s);
    size_t k;

    /* A call to strxfrm costs about 20 times more than a call to strdup of
       the result.  Therefore it is worth to try to avoid calling strxfrm
       more than once on a given string, by making enough room before calling
       strxfrm.  The size of the strxfrm result, k, is likely to be between
       l and 3 * l.  */
    if (3 * l + 1 > allocated)
      {
        /* Grow the result buffer.  */
        if (3 * l + 1 <= sizeof (tmpbuf))
          {
            result = tmpbuf;
            allocated = sizeof (tmpbuf);
          }
        else
          {
            size_t new_allocated;
            char *new_result;

            new_allocated = 3 * l + 1;
            if (new_allocated < 2 * allocated)
              new_allocated = 2 * allocated;
            new_result = (char *) malloc (new_allocated);
            if (new_result != NULL)
              {
                allocated = new_allocated;
                result = new_result;
              }
          }
      }

    errno = 0;
    k = strxfrm (result, s, allocated);
    if (errno != 0)
      goto fail;
    if (k >= allocated)
      {
        /* Grow the result buffer.  */
        if (result != resultbuf && result != tmpbuf)
          free (result);
        if (k + 1 <= sizeof (tmpbuf))
          {
            result = tmpbuf;
            allocated = sizeof (tmpbuf);
          }
        else
          {
            size_t new_allocated;
            char *new_result;

            new_allocated = k + 1;
            new_result = (char *) malloc (new_allocated);
            if (new_result == NULL)
              goto out_of_memory;
            allocated = new_allocated;
            result = new_result;
          }
        /* Here k < allocated.  */

        /* Try again.  */
        errno = 0;
        if (strxfrm (result, s, allocated) != k)
          /* strxfrm() is not producing reproducible results.  */
          abort ();
        if (errno != 0)
          goto fail;
      }

    /* Verify that strxfrm() has NUL-terminated the result.  */
    if (result[k] != '\0')
      abort ();
    length = k + 1;
  }

  /* Here length > 0.  */

  if (result == tmpbuf)
    {
      if (resultbuf != NULL && length <= *lengthp)
        {
          memcpy (resultbuf, result, length);
          result = resultbuf;
        }
      else
        {
          char *memory = (char *) malloc (length);

          if (memory == NULL)
            goto out_of_memory;
          memcpy (memory, result, length);
          result = memory;
        }
    }
  else
    {
      /* Shrink the allocated memory if possible.  */
      if (result != resultbuf && length < allocated)
        {
          if (length <= *lengthp)
            {
              memcpy (resultbuf, result, length);
              free (result);
              result = resultbuf;
            }
          else
            {
              char *memory = (char *) realloc (result, length);
              if (memory != NULL)
                {
                  memcpy (memory, result, length);
                  result = memory;
                }
            }
        }
    }

  *lengthp = length;
  return result;

 fail:
  {
    int saved_errno = errno;
    if (result != resultbuf && result != tmpbuf)
      free (result);
    errno = saved_errno;
    return NULL;
  }

 out_of_memory:
  errno = ENOMEM;
  return NULL;
}
=============================== modules/astrxfrm ===============================
Description:
Locale dependent string transformation for comparison.

Files:
lib/astrxfrm.h
lib/astrxfrm.c

Depends-on:

configure.ac:

Makefile.am:
lib_SOURCES += astrxfrm.c

Include:
"astrxfrm.h"

License:
LGPL

Maintainer:
Bruno Haible



reply via email to

[Prev in Thread] Current Thread [Next in Thread]