New module 'astrxfrm'.
authorBruno Haible <bruno@clisp.org>
Sat, 14 Aug 2010 22:19:47 +0000 (00:19 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 14 Aug 2010 22:19:47 +0000 (00:19 +0200)
ChangeLog
lib/astrxfrm.c [new file with mode: 0644]
lib/astrxfrm.h [new file with mode: 0644]
modules/astrxfrm [new file with mode: 0644]

index 68df573..d78f6ff 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2010-08-14  Bruno Haible  <bruno@clisp.org>
+
+       New module 'astrxfrm'.
+       * lib/astrxfrm.h: New file.
+       * lib/astrxfrm.c: New file, based on lib/memxfrm.c.
+       * modules/astrxfrm: New file.
+
 2010-08-14  Reuben Thomas <rrt@sc3d.org>
 
        regex: Tweak doc.
diff --git a/lib/astrxfrm.c b/lib/astrxfrm.c
new file mode 100644 (file)
index 0000000..00315bb
--- /dev/null
@@ -0,0 +1,181 @@
+/* Locale dependent string transformation for comparison.
+   Copyright (C) 2010 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 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;
+}
diff --git a/lib/astrxfrm.h b/lib/astrxfrm.h
new file mode 100644 (file)
index 0000000..41046a4
--- /dev/null
@@ -0,0 +1,47 @@
+/* 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 */
diff --git a/modules/astrxfrm b/modules/astrxfrm
new file mode 100644 (file)
index 0000000..ff7cfa5
--- /dev/null
@@ -0,0 +1,22 @@
+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