Rewritten.
authordrepper <drepper>
Mon, 20 Apr 1998 18:39:22 +0000 (18:39 +0000)
committerdrepper <drepper>
Mon, 20 Apr 1998 18:39:22 +0000 (18:39 +0000)
iconv/gconv.c
iconv/gconv.h
iconv/gconv_builtin.c
iconv/gconv_builtin.h
iconv/gconv_conf.c
iconv/gconv_int.h
iconv/gconv_open.c
iconv/gconv_simple.c
iconv/iconv.c

index f8b7c80..aa58bdb 100644 (file)
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <assert.h>
 #include <gconv.h>
+#include <sys/param.h>
 
 
 int
 internal_function
-__gconv (gconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf,
-        size_t *outbytesleft, size_t *converted)
+__gconv (gconv_t cd, const char **inbuf, const char *inbufend, char **outbuf,
+        char *outbufend, size_t *converted)
 {
   size_t last_step = cd->nsteps - 1;
-  size_t oldinbytes = *inbytesleft;
   int result;
 
   if (cd == (gconv_t) -1L)
     return GCONV_ILLEGAL_DESCRIPTOR;
 
-  cd->data[last_step].outbuf = outbuf ? *outbuf : NULL;
-  cd->data[last_step].outbufavail = 0;
-  cd->data[last_step].outbufsize = *outbytesleft;
+  assert (converted != NULL);
+  *converted = 0;
 
-  if (converted != NULL)
-    *converted = 0;
+  if (inbuf == NULL || *inbuf == NULL)
+    /* We just flush.  */
+    result = (*cd->steps->fct) (cd->steps, cd->data, NULL, NULL, converted, 1);
+  else
+    {
+      const char *last_start;
 
-  result = (*cd->steps->fct) (cd->steps, cd->data,
-                             inbuf ? *inbuf : NULL, inbytesleft,
-                             converted, inbuf == NULL || *inbuf == NULL);
+      assert (outbuf != NULL && *outbuf != NULL);
+      cd->data[last_step].outbuf = *outbuf;
+      cd->data[last_step].outbufend = outbufend;
 
-  if (inbuf != NULL && *inbuf != NULL)
-    *inbuf += oldinbytes - *inbytesleft;
-  if (outbuf != NULL && *outbuf != NULL)
-    {
-      *outbuf += cd->data[last_step].outbufavail;
-      *outbytesleft -= cd->data[last_step].outbufavail;
+      do
+       {
+         /* See whether the input size is reasoable for the output
+            size.  If not adjust it.  */
+         size_t inlen = ((inbufend - *inbuf) / cd->steps->max_needed_from
+                         * cd->steps->max_needed_from);
+
+         if (cd->nsteps > 1)
+           inlen = MIN (inlen, (((outbufend - cd->data[last_step].outbuf)
+                                 / cd->steps[last_step].max_needed_to)
+                                * cd->steps[last_step].max_needed_to));
+
+         last_start = *inbuf;
+         result = (*cd->steps->fct) (cd->steps, cd->data, inbuf,
+                                     *inbuf + inlen, converted, 0);
+       }
+      while (result == GCONV_EMPTY_INPUT && last_start != *inbuf
+            && *inbuf + cd->steps->min_needed_from <= inbufend);
     }
 
+  if (outbuf != NULL && *outbuf != NULL)
+    *outbuf = cd->data[last_step].outbuf;
+
   return result;
 }
index f3f80f4..cd0e3de 100644 (file)
@@ -57,8 +57,8 @@ struct gconv_loaded_object;
 
 /* Type of a conversion function.  */
 typedef int (*gconv_fct) __P ((struct gconv_step *,
-                              struct gconv_step_data *,
-                              __const char *, size_t *, size_t *, int));
+                              struct gconv_step_data *, __const char **,
+                              __const char *, size_t *, int));
 
 /* Constructor and destructor for local data for conversion step.  */
 typedef int (*gconv_init_fct) __P ((struct gconv_step *));
@@ -80,6 +80,13 @@ struct gconv_step
   gconv_init_fct init_fct;
   gconv_end_fct end_fct;
 
+  /* Information about the number of bytes needed or produced in this
+     step.  This helps optimizing the buffer sizes.  */
+  int min_needed_from;
+  int max_needed_from;
+  int min_needed_to;
+  int max_needed_to;
+
   void *data;          /* Pointer to step-local data.  */
 };
 
@@ -88,8 +95,7 @@ struct gconv_step
 struct gconv_step_data
 {
   char *outbuf;                /* Output buffer for this step.  */
-  size_t outbufavail;  /* Bytes already available in output buffer.  */
-  size_t outbufsize;   /* Size of output buffer.  */
+  char *outbufend;     /* Address of first byte after the output buffer.  */
 
   int is_last;
 
index 6b14804..a970fcc 100644 (file)
@@ -33,15 +33,25 @@ static struct builtin_map
   gconv_init_fct init;
   gconv_end_fct end;
 
+  int min_needed_from;
+  int max_needed_from;
+  int min_needed_to;
+  int max_needed_to;
+
 } map[] =
 {
 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
-                              Fct, Init, End) \
+                              Fct, Init, End, MinF, MaxF, MinT, MaxT) \
   {                                                                          \
     name: Name,                                                                      \
     fct: Fct,                                                                \
     init: Init,                                                                      \
     end: End,                                                                \
+                                                                             \
+    min_needed_from: MinF,                                                   \
+    max_needed_from: MaxF,                                                   \
+    min_needed_to: MinT,                                                     \
+    max_needed_to: MaxT                                                              \
   },
 #define BUILTIN_ALIAS(From, To)
 
@@ -66,4 +76,9 @@ __gconv_get_builtin_trans (const char *name, struct gconv_step *step)
   step->end_fct = map[cnt].end;
   step->counter = INT_MAX;
   step->shlib_handle = NULL;
+
+  step->min_needed_from = map[cnt].min_needed_from;
+  step->max_needed_from = map[cnt].max_needed_from;
+  step->min_needed_to = map[cnt].min_needed_to;
+  step->max_needed_to = map[cnt].max_needed_to;
 }
index 265dca1..3d214ff 100644 (file)
@@ -26,10 +26,12 @@ BUILTIN_ALIAS ("10646-1:1993/UCS4/", "ISO-10646/UCS4/")
 
 BUILTIN_TRANSFORMATION (NULL, "INTERNAL", 8,
                        "ISO-10646/UCS4/", 1, "=INTERNAL->ucs4",
-                       __gconv_transform_internal_ucs4, NULL, NULL)
+                       __gconv_transform_internal_ucs4, NULL, NULL,
+                       4, 4, 4, 4)
 BUILTIN_TRANSFORMATION (NULL, "ISO-10646/UCS4/", 15,
                        "INTERNAL", 1, "=ucs4->INTERNAL",
-                       __gconv_transform_internal_ucs4, NULL, NULL)
+                       __gconv_transform_internal_ucs4, NULL, NULL,
+                       4, 4, 4, 4)
 /* Please note that we need only one function for both direction.  */
 
 BUILTIN_ALIAS ("UTF8//", "ISO-10646/UTF8/")
@@ -37,22 +39,27 @@ BUILTIN_ALIAS ("UTF-8//", "ISO-10646/UTF8/")
 
 BUILTIN_TRANSFORMATION (NULL, "INTERNAL", 8,
                        "ISO-10646/UTF8/", 1, "=INTERNAL->utf8",
-                       __gconv_transform_internal_utf8, NULL, NULL)
+                       __gconv_transform_internal_utf8, NULL, NULL,
+                       4, 4, 1, 6)
 
 BUILTIN_TRANSFORMATION ("ISO-10646/UTF-?8/", "ISO-10646/UTF", 13,
                        "INTERNAL", 1, "=utf8->INTERNAL",
-                       __gconv_transform_utf8_internal, NULL, NULL)
+                       __gconv_transform_utf8_internal, NULL, NULL,
+                       1, 6, 4, 4)
 
 BUILTIN_ALIAS ("UCS2//", "ISO-10646/UCS2/")
 BUILTIN_ALIAS ("UCS-2//", "ISO-10646/UCS2/")
 
 BUILTIN_TRANSFORMATION (NULL, "ISO-10646/UCS2/", 15, "INTERNAL",
                        1, "=ucs2->INTERNAL",
-                       __gconv_transform_ucs2_internal, NULL, NULL)
+                       __gconv_transform_ucs2_internal, NULL, NULL,
+                       2, 2, 4, 4)
 
 BUILTIN_TRANSFORMATION (NULL, "INTERNAL", 8, "ISO-10646/UCS2/",
                        1, "=INTERNAL->ucs2",
-                       __gconv_transform_internal_ucs2, NULL, NULL)
+                       __gconv_transform_internal_ucs2, NULL, NULL,
+                       4, 4, 2, 2)
 
 BUILTIN_TRANSFORMATION ("(.*)", NULL, 0, "\\1", 1, "=dummy",
-                       __gconv_transform_dummy, NULL, NULL)
+                       __gconv_transform_dummy, NULL, NULL,
+                       1, 1, 1, 1)
index c67a0d8..ae5ba19 100644 (file)
@@ -47,7 +47,7 @@ static const char gconv_module_ext[] = MODULE_EXT;
 static struct gconv_module builtin_modules[] =
 {
 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
-                              Fct, Init, End) \
+                              Fct, Init, End, MinF, MaxF, MinT, MaxT) \
   {                                                                          \
     from_pattern: From,                                                              \
     from_constpfx: ConstPfx,                                                 \
@@ -69,7 +69,7 @@ static const char *
 builtin_aliases[] =
 {
 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
-                              Fct, Init, End)
+                              Fct, Init, End, MinF, MaxF, MinT, MaxT)
 #define BUILTIN_ALIAS(From, To) From " " To,
 
 #include "gconv_builtin.h"
index a1475f8..86e892f 100644 (file)
@@ -34,8 +34,8 @@ struct gconv_alias
 };
 
 
-/* Default size of intermediate buffers.  */
-#define GCONV_DEFAULT_BUFSIZE  8160
+/* How many character should be conveted in one call?  */
+#define GCONV_NCHAR_GOAL       8160
 
 
 /* Structure describing one loaded shared object.  This normally are
@@ -99,9 +99,8 @@ extern int __gconv_close (gconv_t cd)
    according to rules described by CD and place up to *OUTBYTESLEFT
    bytes in buffer starting at *OUTBUF.  Return number of written
    characters in *CONVERTED if this pointer is not null.  */
-extern int __gconv (gconv_t __cd, const char **__inbuf, size_t *__inbytesleft,
-                   char **__outbuf, size_t *__outbytesleft,
-                   size_t *__converted)
+extern int __gconv (gconv_t __cd, const char **__inbuf, const char *inbufend,
+                   char **__outbuf, char *outbufend, size_t *__converted)
      internal_function;
 
 /* Return in *HANDLE a pointer to an array with *NSTEPS elements describing
@@ -149,8 +148,8 @@ extern void __gconv_get_builtin_trans (const char *__name,
 #ifdef _LIBC
 # define __BUILTIN_TRANS(Name) \
   extern int Name (struct gconv_step *__step, struct gconv_step_data *__data, \
-                  const char *__inbuf, size_t *__inlen, size_t *__written,   \
-                  int __do_flush)
+                  const char **__inbuf, const char *__inbufend,              \
+                  size_t *__written, int __do_flush)
 
 __BUILTIN_TRANS (__gconv_transform_dummy);
 __BUILTIN_TRANS (__gconv_transform_ascii_internal);
index d82dcfe..831794f 100644 (file)
@@ -62,21 +62,24 @@ __gconv_open (const char *toset, const char *fromset, gconv_t *handle)
              for (cnt = 0; cnt < nsteps; ++cnt)
                {
                  /* If this is the last step we must not allocate an output
-                    buffer.  Signal this to the initializer.  */
+                    buffer.  */
                  data[cnt].is_last = cnt == nsteps - 1;
 
                  /* We use the `mbstate_t' member in DATA.  */
                  data[cnt].statep = &data[cnt].__state;
 
                  /* Allocate the buffer.  */
-                 data[cnt].outbufsize = GCONV_DEFAULT_BUFSIZE;
-                 data[cnt].outbuf = (char *) malloc (data[cnt].outbufsize);
-                 if (data[cnt].outbuf == NULL)
+                 if (!data[cnt].is_last)
                    {
-                     res = GCONV_NOMEM;
-                     break;
+                     data[cnt].outbuf =
+                       (char *) malloc (GCONV_NCHAR_GOAL
+                                        * steps[cnt].max_needed_to);
+                     if (data[cnt].outbuf == NULL)
+                       {
+                         res = GCONV_NOMEM;
+                         break;
+                       }
                    }
-                 data[cnt].outbufavail = 0;
                }
            }
        }
index b72e61e..f2fec12 100644 (file)
@@ -35,7 +35,7 @@
 
 /* These are definitions used by some of the functions for handling
    UTF-8 encoding below.  */
-static const wchar_t encoding_mask[] =
+static const uint32_t encoding_mask[] =
 {
   ~0x7ff, ~0xffff, ~0x1fffff, ~0x3ffffff
 };
@@ -49,8 +49,8 @@ static const unsigned char encoding_byte[] =
 
 int
 __gconv_transform_dummy (struct gconv_step *step, struct gconv_step_data *data,
-                        const char *inbuf, size_t *inlen, size_t *written,
-                        int do_flush)
+                        const char **inbuf, const char *inbufend,
+                        size_t *written, int do_flush)
 {
   size_t do_write;
 
@@ -60,12 +60,12 @@ __gconv_transform_dummy (struct gconv_step *step, struct gconv_step_data *data,
     do_write = 0;
   else
     {
-      do_write = MIN (*inlen, data->outbufsize - data->outbufavail);
+      do_write = MIN (inbufend - *inbuf, data->outbufend - data->outbuf);
 
       memcpy (data->outbuf, inbuf, do_write);
 
-      *inlen -= do_write;
-      data->outbufavail += do_write;
+      *inbuf -= do_write;
+      *data->outbuf += do_write;
     }
 
   /* ### TODO Actually, this number must be devided according to the
@@ -83,934 +83,330 @@ __gconv_transform_dummy (struct gconv_step *step, struct gconv_step_data *data,
    format is, if any, the endianess.  The Unicode/ISO 10646 says that
    unless some higher protocol specifies it differently, the byte
    order is big endian.*/
-int
-__gconv_transform_internal_ucs4 (struct gconv_step *step,
-                                 struct gconv_step_data *data,
-                                 const char *inbuf, size_t *inlen,
-                                 size_t *written, int do_flush)
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                4
+#define MIN_NEEDED_TO          4
+#define FROM_DIRECTION         1
+#define FROM_LOOP              internal_ucs4_loop
+#define TO_LOOP                        internal_ucs4_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_internal_ucs4
+
+
+static inline int
+internal_ucs4_loop (const unsigned char **inptrp, const unsigned char *inend,
+                   unsigned char **outptrp, unsigned char *outend,
+                   mbstate_t *state, void *data, size_t *converted)
 {
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write = 0;
+  const unsigned char *inptr = *inptrp;
+  unsigned char *outptr = *outptrp;
+  size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
   int result;
 
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-
-         /* Clear output buffer.  */
-         data->outbufavail = 0;
-       }
-    }
-  else
-    {
-      int save_errno = errno;
-
-      result = GCONV_OK;
-      do
-       {
-         size_t n_convert = (MIN (*inlen,
-                                  (data->outbufsize - data->outbufavail))
-                             / sizeof (wchar_t));
-
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-         /* Sigh, we have to do some real work.  */
-         wchar_t *outbuf = (wchar_t *) &data->outbuf[data->outbufavail];
-         size_t cnt;
+  /* Sigh, we have to do some real work.  */
+  size_t cnt;
 
-         for (cnt = 0; cnt < n_convert; ++cnt)
-           outbuf[cnt] = bswap_32 (((wchar_t *) inbuf)[cnt]);
+  for (cnt = 0; cnt < n_convert; ++cnt)
+    *((uint32_t *) outptr)++ = bswap_32 (*((uint32_t *) inptr)++);
 
+  *inptrp = inptr;
+  *outptrp = outptr;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-         /* Simply copy the data.  */
-         memcpy (&data->outbuf[data->outbufsize], inbuf,
-                 n_convert * sizeof (wchar_t));
+  /* Simply copy the data.  */
+  *inptrp = inptr + n_convert * 4;
+  *outptrp = __mempcpy (outptr, inptr, n_convert * 4);
 #else
 # error "This endianess is not supported."
 #endif
 
-         *inlen -= n_convert * sizeof (wchar_t);
-         inbuf += n_convert * sizeof (wchar_t);
-         data->outbufavail += n_convert * sizeof (wchar_t);
-         do_write += n_convert;
-
-         if (*inlen > 0 && *inlen < sizeof (wchar_t))
-           {
-             /* We have an incomplete character at the end.  */
-             result = GCONV_INCOMPLETE_INPUT;
-             break;
-           }
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = (*inlen < sizeof (wchar_t)
-                       ? GCONV_EMPTY_INPUT : GCONV_FULL_OUTPUT);
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen >= sizeof (wchar_t) && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
-
-  if (written != NULL && data->is_last)
-    *written = do_write;
-
-  return result;
-}
-
-
-/* Convert from ISO 646-IRV to the internal (UCS4-like) format.  */
-int
-__gconv_transform_ascii_internal (struct gconv_step *step,
-                                 struct gconv_step_data *data,
-                                 const char *inbuf, size_t *inlen,
-                                 size_t *written, int do_flush)
-{
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write = 0;
-  int result;
-
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-
-         /* Clear output buffer.  */
-         data->outbufavail = 0;
-       }
-    }
-  else
-    {
-      const unsigned char *newinbuf = inbuf;
-      int save_errno = errno;
-
-      result = GCONV_OK;
-      do
-       {
-         size_t actually = 0;
-         size_t cnt = 0;
-
-         while (data->outbufavail + sizeof (wchar_t) <= data->outbufsize
-                && cnt < *inlen)
-           {
-             if (*newinbuf > '\x7f')
-               {
-                 /* This is no correct ANSI_X3.4-1968 character.  */
-                 result = GCONV_ILLEGAL_INPUT;
-                 break;
-               }
-
-             /* It's an one byte sequence.  */
-             *(wchar_t *) &data->outbuf[data->outbufavail]
-               = (wchar_t) *newinbuf;
-             data->outbufavail += sizeof (wchar_t);
-             ++actually;
-
-             ++newinbuf;
-             ++cnt;
-           }
-
-         /* Remember how much we converted.  */
-         do_write += cnt * sizeof (wchar_t);
-         *inlen -= cnt;
-
-         /* Check whether an illegal character appeared.  */
-         if (result != GCONV_OK)
-           break;
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = (*inlen == 0 ? GCONV_EMPTY_INPUT : GCONV_FULL_OUTPUT);
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
-
-  if (written != NULL && data->is_last)
-    *written = do_write / sizeof (wchar_t);
-
-  return result;
-}
-
-
-/* Convert from ISO 10646/UCS to ISO 646-IRV.  */
-int
-__gconv_transform_internal_ascii (struct gconv_step *step,
-                                 struct gconv_step_data *data,
-                                 const char *inbuf, size_t *inlen,
-                                 size_t *written, int do_flush)
-{
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write;
-  int result;
-
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-      do_write = 0;
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-
-         /* Clear output buffer.  */
-         data->outbufavail = 0;
-       }
-    }
-  else
-    {
-      const wchar_t *newinbuf = (const wchar_t *) inbuf;
-      int save_errno = errno;
-      do_write = 0;
-
-      result = GCONV_OK;
-      do
-       {
-         size_t actually = 0;
-         size_t cnt = 0;
-
-         while (data->outbufavail < data->outbufsize
-                && cnt + 3 < *inlen)
-           {
-             if (*newinbuf < L'\0' || *newinbuf > L'\x7f')
-               {
-                 /* This is no correct ANSI_X3.4-1968 character.  */
-                 result = GCONV_ILLEGAL_INPUT;
-                 break;
-               }
-
-             /* It's an one byte sequence.  */
-             data->outbuf[data->outbufavail++] = (char) *newinbuf;
-             ++actually;
-
-             ++newinbuf;
-             cnt += sizeof (wchar_t);
-           }
-
-         /* Remember how much we converted.  */
-         do_write += cnt / sizeof (wchar_t);
-         *inlen -= cnt;
-
-         /* Check whether an illegal character appeared.  */
-         if (result != GCONV_OK)
-           break;
-
-         /* Check for incomplete input.  */
-         if (*inlen > 0 && *inlen < sizeof (wchar_t))
-           {
-             /* We have an incomplete character at the end.  */
-             result = GCONV_INCOMPLETE_INPUT;
-             break;
-           }
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = *inlen == 0 ? GCONV_EMPTY_INPUT : GCONV_FULL_OUTPUT;
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
-
-  if (written != NULL && data->is_last)
-    *written = do_write;
-
-  return result;
-}
-
-
-int
-__gconv_transform_internal_utf8 (struct gconv_step *step,
-                                struct gconv_step_data *data,
-                                const char *inbuf, size_t *inlen,
-                                size_t *written, int do_flush)
-{
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write;
-  int result;
-
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-      do_write = 0;
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-
-         /* Clear output buffer.  */
-         data->outbufavail = 0;
-       }
-    }
-  else
-    {
-      const wchar_t *newinbuf = (const wchar_t *) inbuf;
-      int save_errno = errno;
-      do_write = 0;
-
-      result = GCONV_OK;
-      do
-       {
-         size_t cnt = 0;
-
-         while (data->outbufavail < data->outbufsize
-                && cnt * sizeof (wchar_t) + 3 < *inlen)
-           {
-             wchar_t wc = newinbuf[cnt];
-
-             if (wc < 0 && wc > 0x7fffffff)
-               {
-                 /* This is no correct ISO 10646 character.  */
-                 result = GCONV_ILLEGAL_INPUT;
-                 break;
-               }
-
-             if (wc < 0x80)
-               /* It's an one byte sequence.  */
-               data->outbuf[data->outbufavail++] = (char) wc;
-             else
-               {
-                 size_t step;
-                 size_t start;
-
-                 for (step = 2; step < 6; ++step)
-                   if ((wc & encoding_mask[step - 2]) == 0)
-                     break;
-
-                 if (data->outbufavail + step >= data->outbufsize)
-                   /* Too long.  */
-                   break;
-
-                 start = data->outbufavail;
-                 data->outbufavail += step;
-                 data->outbuf[start] = encoding_byte[step - 2];
-                 --step;
-                 do
-                   {
-                     data->outbuf[start + step] = 0x80 | (wc & 0x3f);
-                     wc >>= 6;
-                   }
-                 while (--step > 0);
-                 data->outbuf[start] |= wc;
-               }
-
-             ++cnt;
-           }
-
-         /* Remember how much we converted.  */
-         do_write += cnt;
-         *inlen -= cnt * sizeof (wchar_t);
-         newinbuf += cnt;
-
-         /* Check whether an illegal character appeared.  */
-         if (result != GCONV_OK)
-           break;
-
-         /* Check for incomplete input.  */
-         if (*inlen > 0 && *inlen < sizeof (wchar_t))
-           {
-             /* We have an incomplete character at the end.  */
-             result = GCONV_INCOMPLETE_INPUT;
-             break;
-           }
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = *inlen == 0 ? GCONV_EMPTY_INPUT : GCONV_FULL_OUTPUT;
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
-
-  if (written != NULL && data->is_last)
-    *written = do_write;
-
-  return result;
-}
-
-
-int
-__gconv_transform_utf8_internal (struct gconv_step *step,
-                                struct gconv_step_data *data,
-                                const char *inbuf, size_t *inlen,
-                                size_t *written, int do_flush)
-{
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write;
-  int result;
-
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-      do_write = 0;
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-       }
-    }
+  /* Determine the status.  */
+  if (*outptrp == outend)
+    result = GCONV_FULL_OUTPUT;
+  else if (*inptrp == inend)
+    result = GCONV_EMPTY_INPUT;
   else
-    {
-      int save_errno = errno;
-      int extra = 0;
-      do_write = 0;
-
-      result = GCONV_OK;
-      do
-       {
-         wchar_t *outbuf = (wchar_t *) &data->outbuf[data->outbufavail];
-         size_t cnt = 0;
-         size_t actually = 0;
-
-         while (data->outbufavail + sizeof (wchar_t) <= data->outbufsize
-                && cnt < *inlen)
-           {
-             size_t start = cnt;
-             wchar_t value;
-             unsigned char byte;
-             int count;
-
-             /* Next input byte.  */
-             byte = inbuf[cnt++];
-
-             if (byte < 0x80)
-               {
-                 /* One byte sequence.  */
-                 count = 0;
-                 value = byte;
-               }
-             else if ((byte & 0xe0) == 0xc0)
-               {
-                 count = 1;
-                 value = byte & 0x1f;
-               }
-             else if ((byte & 0xf0) == 0xe0)
-               {
-                 /* We expect three bytes.  */
-                 count = 2;
-                 value = byte & 0x0f;
-               }
-             else if ((byte & 0xf8) == 0xf0)
-               {
-                 /* We expect four bytes.  */
-                 count = 3;
-                 value = byte & 0x07;
-               }
-             else if ((byte & 0xfc) == 0xf8)
-               {
-                 /* We expect five bytes.  */
-                 count = 4;
-                 value = byte & 0x03;
-               }
-             else if ((byte & 0xfe) == 0xfc)
-               {
-                 /* We expect six bytes.  */
-                 count = 5;
-                 value = byte & 0x01;
-               }
-             else
-               {
-                 /* This is an illegal encoding.  */
-                 result = GCONV_ILLEGAL_INPUT;
-                 break;
-               }
-
-             if (cnt + count > *inlen)
-               {
-                 /* We don't have enough input.  */
-                 --cnt;
-                 extra = count;
-                 break;
-               }
-
-             /* Read the possible remaining bytes.  */
-             while (count > 0)
-               {
-                 byte = inbuf[cnt++];
-                 --count;
-
-                 if ((byte & 0xc0) != 0x80)
-                   {
-                     /* This is an illegal encoding.  */
-                     result = GCONV_ILLEGAL_INPUT;
-                     break;
-                   }
-
-                 value <<= 6;
-                 value |= byte & 0x3f;
-               }
-
-             if (result != GCONV_OK)
-               {
-                 cnt = start;
-                 break;
-               }
-
-             *outbuf++ = value;
-             ++actually;
-           }
-
-         /* Remember how much we converted.  */
-         do_write += actually;
-         *inlen -= cnt;
-         inbuf += cnt;
-
-         data->outbufavail += actually * sizeof (wchar_t);
-
-         /* Check whether an illegal character appeared.  */
-         if (result != GCONV_OK)
-           {
-             result = GCONV_ILLEGAL_INPUT;
-             break;
-           }
-
-         if (*inlen > 0 && *inlen < extra)
-           {
-             /* We have an incomplete character at the end.  */
-             result = GCONV_INCOMPLETE_INPUT;
-             break;
-           }
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = (data->outbufavail + sizeof (wchar_t) > data->outbufsize
-                       ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
+    result = GCONV_INCOMPLETE_INPUT;
 
-  if (written != NULL && data->is_last)
-    *written = do_write;
+  if (converted != NULL)
+    converted += n_convert;
 
   return result;
 }
 
+#include <iconv/skeleton.c>
 
-int
-__gconv_transform_ucs2_internal (struct gconv_step *step,
-                                struct gconv_step_data *data,
-                                const char *inbuf, size_t *inlen,
-                                size_t *written, int do_flush)
-{
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write;
-  int result;
-
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-      do_write = 0;
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-       }
-    }
-  else
-    {
-      const uint16_t *newinbuf = (const uint16_t *) inbuf;
-      int save_errno = errno;
-      do_write = 0;
-
-      do
-       {
-         wchar_t *outbuf = (wchar_t *) &data->outbuf[data->outbufavail];
-         size_t actually = 0;
-
-         errno = 0;
 
-         while (data->outbufavail + 4 <= data->outbufsize
-                && *inlen >= 2)
-           {
+/* Convert from ISO 646-IRV to the internal (UCS4-like) format.  */
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                1
+#define MIN_NEEDED_TO          4
+#define FROM_DIRECTION         1
+#define FROM_LOOP              ascii_internal_loop
+#define TO_LOOP                        ascii_internal_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_ascii_internal
+
+#define MIN_NEEDED_INPUT       MIN_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT      MIN_NEEDED_TO
+#define LOOPFCT                        FROM_LOOP
+#define BODY \
+  {                                                                          \
+    if (*inptr > '\x7f')                                                     \
+      {                                                                              \
+       /* This is no correct ANSI_X3.4-1968 character.  */                   \
+       result = GCONV_ILLEGAL_INPUT;                                         \
+       break;                                                                \
+      }                                                                              \
+                                                                             \
+    /* It's an one byte sequence.  */                                        \
+    *((uint32_t *) outptr)++ = *inptr++;                                     \
+  }
+#include <iconv/loop.c>
+#include <iconv/skeleton.c>
+
+
+/* Convert from the internal (UCS4-like) format to ISO 646-IRV.  */
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                4
+#define MIN_NEEDED_TO          1
+#define FROM_DIRECTION         1
+#define FROM_LOOP              internal_ascii_loop
+#define TO_LOOP                        internal_ascii_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_internal_ascii
+
+#define MIN_NEEDED_INPUT       MIN_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT      MIN_NEEDED_TO
+#define LOOPFCT                        FROM_LOOP
+#define BODY \
+  {                                                                          \
+    if (*((uint32_t *) inptr) > '\x7f')                                              \
+      {                                                                              \
+       /* This is no correct ANSI_X3.4-1968 character.  */                   \
+       result = GCONV_ILLEGAL_INPUT;                                         \
+       break;                                                                \
+      }                                                                              \
+                                                                             \
+    /* It's an one byte sequence.  */                                        \
+    *outptr++ = *((uint32_t *) inptr)++;                                     \
+  }
+#include <iconv/loop.c>
+#include <iconv/skeleton.c>
+
+
+/* Convert from the internal (UCS4-like) format to UTF-8.  */
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                4
+#define MIN_NEEDED_TO          1
+#define MAX_NEEDED_TO          6
+#define FROM_DIRECTION         1
+#define FROM_LOOP              internal_utf8_loop
+#define TO_LOOP                        internal_utf8_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_internal_utf8
+
+#define MIN_NEEDED_INPUT       MIN_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT      MIN_NEEDED_TO
+#define LOOPFCT                        FROM_LOOP
+#define BODY \
+  {                                                                          \
+    uint32_t wc = *((uint32_t *) inptr);                                     \
+                                                                             \
+    /* Since we control every character we read this cannot happen.  */              \
+    assert (wc <= 0x7fffffff);                                               \
+                                                                             \
+    if (wc < 0x80)                                                           \
+      /* It's an one byte sequence.  */                                              \
+      *outptr++ = (unsigned char) wc;                                        \
+    else                                                                     \
+      {                                                                              \
+       size_t step;                                                          \
+       char *start;                                                          \
+                                                                             \
+       for (step = 2; step < 6; ++step)                                      \
+         if ((wc & encoding_mask[step - 2]) == 0)                            \
+           break;                                                            \
+                                                                             \
+       if (outptr + step >= outend)                                          \
+         {                                                                   \
+           /* Too long.  */                                                  \
+           result = GCONV_FULL_OUTPUT;                                       \
+           break;                                                            \
+         }                                                                   \
+                                                                             \
+       start = outptr;                                                       \
+       *outptr = encoding_byte[step - 2];                                    \
+       outptr += step;                                                       \
+       --step;                                                               \
+       do                                                                    \
+         {                                                                   \
+           start[step] = 0x80 | (wc & 0x3f);                                 \
+           wc >>= 6;                                                         \
+         }                                                                   \
+       while (--step > 0);                                                   \
+       start[0] |= wc;                                                       \
+      }                                                                              \
+                                                                             \
+    inptr += 4;                                                                      \
+  }
+#include <iconv/loop.c>
+#include <iconv/skeleton.c>
+
+
+/* Convert from UTF-8 to the internal (UCS4-like) format.  */
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                1
+#define MAX_NEEDED_FROM                6
+#define MIN_NEEDED_TO          4
+#define FROM_DIRECTION         1
+#define FROM_LOOP              utf8_internal_loop
+#define TO_LOOP                        utf8_internal_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_utf8_internal
+
+#define MIN_NEEDED_INPUT       MIN_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT      MIN_NEEDED_TO
+#define LOOPFCT                        FROM_LOOP
+#define BODY \
+  {                                                                          \
+    uint32_t ch;                                                             \
+    uint_fast32_t cnt;                                                       \
+    uint_fast32_t i;                                                         \
+                                                                             \
+    /* Next input byte.  */                                                  \
+    ch = *inptr;                                                             \
+                                                                             \
+    if (ch < 0x80)                                                           \
+      /* One byte sequence.  */                                                      \
+      cnt = 1;                                                               \
+    else if ((ch & 0xe0) == 0xc0)                                            \
+      {                                                                              \
+       cnt = 2;                                                              \
+       ch &= 0x1f;                                                           \
+      }                                                                              \
+    else if ((ch & 0xf0) == 0xe0)                                            \
+      {                                                                              \
+       /* We expect three bytes.  */                                         \
+       cnt = 3;                                                              \
+       ch &= 0x0f;                                                           \
+      }                                                                              \
+    else if ((ch & 0xf8) == 0xf0)                                            \
+      {                                                                              \
+       /* We expect four bytes.  */                                          \
+       cnt = 4;                                                              \
+       ch &= 0x07;                                                           \
+      }                                                                              \
+    else if ((ch & 0xfc) == 0xf8)                                            \
+      {                                                                              \
+       /* We expect five bytes.  */                                          \
+       cnt = 5;                                                              \
+       ch &= 0x03;                                                           \
+      }                                                                              \
+    else if ((ch & 0xfe) == 0xfc)                                            \
+      {                                                                              \
+       /* We expect six bytes.  */                                           \
+       cnt = 6;                                                              \
+       ch &= 0x01;                                                           \
+      }                                                                              \
+    else                                                                     \
+      {                                                                              \
+       /* This is an illegal encoding.  */                                   \
+       result = GCONV_ILLEGAL_INPUT;                                         \
+       break;                                                                \
+      }                                                                              \
+                                                                             \
+    if (NEED_LENGTH_TEST && inptr + cnt >= inend)                            \
+      {                                                                              \
+       /* We don't have enough input.  */                                    \
+       result = GCONV_INCOMPLETE_INPUT;                                      \
+       break;                                                                \
+      }                                                                              \
+                                                                             \
+    /* Read the possible remaining bytes.  */                                \
+    for (i = 1; i < cnt; ++i)                                                \
+      {                                                                              \
+       uint32_t byte = inptr[i];                                             \
+                                                                             \
+       if ((byte & 0xc0) != 0x80)                                            \
+         {                                                                   \
+           /* This is an illegal encoding.  */                               \
+           result = GCONV_ILLEGAL_INPUT;                                     \
+           break;                                                            \
+         }                                                                   \
+                                                                             \
+       ch <<= 6;                                                             \
+       ch |= byte & 0x3f;                                                    \
+      }                                                                              \
+                                                                             \
+    /* Now adjust the pointers and store the result.  */                     \
+    inptr += cnt;                                                            \
+    *((uint32_t *) outptr)++ = ch;                                           \
+  }
+#include <iconv/loop.c>
+#include <iconv/skeleton.c>
+
+
+/* Convert from UCS2 to the internal (UCS4-like) format.  */
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                2
+#define MIN_NEEDED_TO          4
+#define FROM_DIRECTION         1
+#define FROM_LOOP              ucs2_internal_loop
+#define TO_LOOP                        ucs2_internal_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_ucs2_internal
+
+#define MIN_NEEDED_INPUT       MIN_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT      MIN_NEEDED_TO
+#define LOOPFCT                        FROM_LOOP
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-             outbuf[actually++] = (wchar_t) bswap_16 (*newinbuf++);
+# define BODY \
+  *((uint32_t *) outptr)++ = bswap_16 (*((uint16_t *) inptr)++);
 #else
-             outbuf[actually++] = (wchar_t) *newinbuf++;
+# define BODY \
+  *((uint32_t *) outptr)++ = *((uint16_t *) inptr)++;
 #endif
-             data->outbufavail += 4;
-             *inlen -= 2;
-           }
-
-         /* Remember how much we converted.  */
-         do_write += actually * sizeof (wchar_t);
-
-         if (*inlen == 1)
-           {
-             /* We have an incomplete character at the end.  */
-             result = GCONV_INCOMPLETE_INPUT;
-             break;
-           }
-
-         /* Check whether an illegal character appeared.  */
-         if (errno != 0)
-           {
-             result = GCONV_ILLEGAL_INPUT;
-             break;
-           }
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = (data->outbufavail + sizeof (wchar_t) > data->outbufsize
-                       ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
-
-  if (written != NULL && data->is_last)
-    *written = do_write;
-
-  return result;
-}
-
-
-int
-__gconv_transform_internal_ucs2 (struct gconv_step *step,
-                                struct gconv_step_data *data,
-                                const char *inbuf, size_t *inlen,
-                                size_t *written, int do_flush)
-{
-  struct gconv_step *next_step = step + 1;
-  struct gconv_step_data *next_data = data + 1;
-  gconv_fct fct = next_step->fct;
-  size_t do_write;
-  int result;
-
-  /* If the function is called with no input this means we have to reset
-     to the initial state.  The possibly partly converted input is
-     dropped.  */
-  if (do_flush)
-    {
-      /* Clear the state.  */
-      memset (data->statep, '\0', sizeof (mbstate_t));
-      do_write = 0;
-
-      /* Call the steps down the chain if there are any.  */
-      if (data->is_last)
-       result = GCONV_OK;
-      else
-       {
-         struct gconv_step *next_step = step + 1;
-         struct gconv_step_data *next_data = data + 1;
-
-         result = (*fct) (next_step, next_data, NULL, 0, written, 1);
-
-         /* Clear output buffer.  */
-         data->outbufavail = 0;
-       }
-    }
-  else
-    {
-      const wchar_t *newinbuf = (const wchar_t *) inbuf;
-      int save_errno = errno;
-      do_write = 0;
-
-      do
-       {
-         uint16_t *outbuf = (uint16_t *) &data->outbuf[data->outbufavail];
-         size_t actually = 0;
-
-         errno = 0;
-
-         while (data->outbufavail + 2 <= data->outbufsize
-                && *inlen >= 4)
-           {
-             if (*newinbuf >= 0x10000)
-               {
-                 __set_errno (EILSEQ);
-                   break;
-               }
+#include <iconv/loop.c>
+#include <iconv/skeleton.c>
+
+
+/* Convert from the internal (UCS4-like) format to UCS2.  */
+#define DEFINE_INIT            0
+#define DEFINE_FINI            0
+#define MIN_NEEDED_FROM                4
+#define MIN_NEEDED_TO          2
+#define FROM_DIRECTION         1
+#define FROM_LOOP              internal_ucs2_loop
+#define TO_LOOP                        internal_ucs2_loop /* This is not used.  */
+#define FUNCTION_NAME          __gconv_transform_internal_ucs2
+
+#define MIN_NEEDED_INPUT       MIN_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT      MIN_NEEDED_TO
+#define LOOPFCT                        FROM_LOOP
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-             /* Please note that we use the `uint32_t' pointer as a
-                `uint16_t' pointer which works since we are on a
-                little endian machine.  */
-             outbuf[actually++] = bswap_16 (*((uint16_t *) newinbuf));
-             ++newinbuf;
+# define BODY \
+  {                                                                          \
+    if (*((uint32_t *) inptr) >= 0x10000)                                    \
+      {                                                                              \
+       result = GCONV_ILLEGAL_INPUT;                                         \
+       break;                                                                \
+      }                                                                              \
+    /* Please note that we use the `uint32_t' from-pointer as an `uint16_t'   \
+       pointer which works since we are on a little endian machine.  */              \
+    *((uint16_t *) outptr)++ = bswap_16 (*((uint16_t *) inptr));             \
+    inptr += 4;                                                                      \
+  }
 #else
-             outbuf[actually++] = *newinbuf++;
+# define BODY \
+  {                                                                          \
+    if (*((uint32_t *) inptr) >= 0x10000)                                    \
+      {                                                                              \
+       result = GCONV_ILLEGAL_INPUT;                                         \
+       break;                                                                \
+      }                                                                              \
+    *((uint16_t *) outptr)++ = *((uint32_t *) inptr)++;                              \
+  }
 #endif
-             *inlen -= 4;
-             data->outbufavail += 2;
-           }
-
-         /* Remember how much we converted.  */
-         do_write += (const char *) newinbuf - inbuf;
-
-         if (*inlen > 0 && *inlen < 4)
-           {
-             /* We have an incomplete input character.  */
-             result = GCONV_INCOMPLETE_INPUT;
-             break;
-           }
-
-         /* Check whether an illegal character appeared.  */
-         if (errno != 0)
-           {
-             result = GCONV_ILLEGAL_INPUT;
-             break;
-           }
-
-         if (data->is_last)
-           {
-             /* This is the last step.  */
-             result = *inlen == 0 ? GCONV_EMPTY_INPUT : GCONV_FULL_OUTPUT;
-             break;
-           }
-
-         /* Status so far.  */
-         result = GCONV_EMPTY_INPUT;
-
-         if (data->outbufavail > 0)
-           {
-             /* Call the functions below in the chain.  */
-             size_t newavail = data->outbufavail;
-
-             result = (*fct) (next_step, next_data, data->outbuf, &newavail,
-                              written, 0);
-
-             /* Correct the output buffer.  */
-             if (newavail != data->outbufavail && newavail > 0)
-               {
-                 memmove (data->outbuf,
-                          &data->outbuf[data->outbufavail - newavail],
-                          newavail);
-                 data->outbufavail = newavail;
-               }
-           }
-       }
-      while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
-
-      __set_errno (save_errno);
-    }
-
-  if (written != NULL && data->is_last)
-    *written = do_write / sizeof (wchar_t);
-
-  return result;
-}
+#include <iconv/loop.c>
+#include <iconv/skeleton.c>
index fc0ed41..2f57295 100644 (file)
@@ -32,10 +32,27 @@ iconv (iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf,
        size_t *outbytesleft)
 {
   gconv_t gcd = (gconv_t) cd;
+  char *outstart = outbuf ? *outbuf : NULL;
   size_t converted;
   int result;
 
-  result = __gconv (gcd, inbuf, inbytesleft, outbuf, outbytesleft, &converted);
+  if (inbuf == NULL || *inbuf == NULL)
+    {
+      result = __gconv (gcd, NULL, NULL, outbuf, outstart + *outbytesleft,
+                       &converted);
+    }
+  else
+    {
+      const char *instart = *inbuf;
+
+      result = __gconv (gcd, inbuf, *inbuf + *inbytesleft, outbuf,
+                       *outbuf + *outbytesleft, &converted);
+
+      *inbytesleft -= *inbuf - instart;
+    }
+  if (outstart != NULL)
+    *outbytesleft -= *outbuf - outstart;
+
   switch (result)
     {
     case GCONV_ILLEGAL_DESCRIPTOR: