Optimize a bit more.
[kopensolaris-gnu/glibc.git] / iconv / skeleton.c
1 /* Skeleton for a converison module.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* This file can be included to provide definitions of several things
22    many modules have in common.  It can be customized using the following
23    macros:
24
25      DEFINE_INIT        define the default initializer.  This requires the
26                         following symbol to be defined.
27
28      CHARSET_NAME       string with official name of the coded character
29                         set (in all-caps)
30
31      DEFINE_FINI        define the default destructor function.
32
33      MIN_NEEDED_FROM    minimal number of bytes needed for the from-charset.
34      MIN_NEEDED_TO      likewise for the to-charset.
35
36      MAX_NEEDED_FROM    maximal number of bytes needed for the from-charset.
37                         This macro is optional, it defaults to MIN_NEEDED_FROM.
38      MAX_NEEDED_TO      likewise for the to-charset.
39
40      DEFINE_DIRECTION_OBJECTS
41                         two objects will be defined to be used when the
42                         `gconv' function must only distinguish two
43                         directions.  This is implied by DEFINE_INIT.
44                         If this macro is not defined the following
45                         macro must be available.
46
47      FROM_DIRECTION     this macro is supposed to return a value != 0
48                         if we convert from the current character set,
49                         otherwise it return 0.
50
51      EMIT_SHIFT_TO_INIT this symbol is optional.  If it is defined it
52                         defines some code which writes out a sequence
53                         of characters which bring the current state into
54                         the initial state.
55
56      FROM_LOOP          name of the function implementing the conversion
57                         from the current characters.
58      TO_LOOP            likewise for the other direction
59
60      RESET_STATE        in case of an error we must reset the state for
61                         the rerun so this macro must be defined for
62                         stateful encodings.  It takes an argument which
63                         is nonzero when saving.
64
65      RESET_INPUT_BUFFER If the input character sets allow this the macro
66                         can be defined to reset the input buffer pointers
67                         to cover only those characters up to the error.
68
69      FUNCTION_NAME      if not set the conversion function is named `gconv'.
70
71      PREPARE_LOOP       optional code preparing the conversion loop.  Can
72                         contain variable definitions.
73      END_LOOP           also optional, may be used to store information
74
75      EXTRA_LOOP_ARGS    optional macro specifying extra arguments passed
76                         to loop function.
77  */
78
79 #include <assert.h>
80 #include <gconv.h>
81 #include <string.h>
82 #define __need_size_t
83 #define __need_NULL
84 #include <stddef.h>
85
86
87 /* The direction objects.  */
88 #if DEFINE_DIRECTION_OBJECTS || DEFINE_INIT
89 static int from_object;
90 static int to_object;
91
92 # ifndef FROM_DIRECTION
93 #  define FROM_DIRECTION step->data == &from_object
94 # endif
95 #else
96 # ifndef FROM_DIRECTION
97 #  error "FROM_DIRECTION must be provided if direction objects are not used"
98 # endif
99 #endif
100
101
102 /* How many bytes are needed at most for the from-charset.  */
103 #ifndef MAX_NEEDED_FROM
104 # define MAX_NEEDED_FROM        MIN_NEEDED_FROM
105 #endif
106
107 /* Same for the to-charset.  */
108 #ifndef MAX_NEEDED_TO
109 # define MAX_NEEDED_TO          MIN_NEEDED_TO
110 #endif
111
112
113 /* For conversions from a fixed width character sets to another fixed width
114    character set we we can define RESET_INPUT_BUFFER is necessary.  */
115 #if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE
116 # if MIN_NEEDED_FROM == MAX_NEEDED_FROM && MIN_NEEDED_TO == MAX_NEEDED_TO
117 /* We have to used these `if's here since the compiler cannot know that
118    (outbuf - outerr) is always divisible by MIN_NEEDED_TO.  */
119 #  define RESET_INPUT_BUFFER \
120   if (MIN_NEEDED_FROM % MIN_NEEDED_TO == 0)                                   \
121     *inbuf -= (outbuf - outerr) * (MIN_NEEDED_FROM / MIN_NEEDED_TO);          \
122   else if (MIN_NEEDED_TO % MIN_NEEDED_FROM == 0)                              \
123     *inbuf -= (outbuf - outerr) / (MIN_NEEDED_TO / MIN_NEEDED_FROM);          \
124   else                                                                        \
125     *inbuf -= ((outbuf - outerr) / MIN_NEEDED_TO) * MIN_NEEDED_FROM
126 # endif
127 #endif
128
129
130 /* The default init function.  It simply matches the name and initializes
131    the step data to point to one of the objects above.  */
132 #if DEFINE_INIT
133 # ifndef CHARSET_NAME
134 #  error "CHARSET_NAME not defined"
135 # endif
136
137 int
138 gconv_init (struct gconv_step *step)
139 {
140   /* Determine which direction.  */
141   if (__strcasecmp (step->from_name, CHARSET_NAME) == 0)
142     step->data = &from_object;
143   else if (__strcasecmp (step->to_name, CHARSET_NAME) == 0)
144     step->data = &to_object;
145   else
146     return GCONV_NOCONV;
147
148   if (step->data == &from_object)
149     {
150       step->min_needed_from = MIN_NEEDED_FROM;
151       step->max_needed_from = MAX_NEEDED_FROM;
152       step->min_needed_to = MIN_NEEDED_TO;
153       step->max_needed_to = MAX_NEEDED_TO;
154     }
155   else
156     {
157       step->min_needed_from = MIN_NEEDED_TO;
158       step->max_needed_from = MAX_NEEDED_TO;
159       step->min_needed_to = MIN_NEEDED_FROM;
160       step->max_needed_to = MAX_NEEDED_FROM;
161     }
162
163 #ifdef RESET_STATE
164   step->stateful = 1;
165 #else
166   step->stateful = 0;
167 #endif
168
169   return GCONV_OK;
170 }
171 #endif
172
173
174 /* The default destructor function does nothing in the moment and so
175    be define it at all.  But we still provide the macro just in case
176    we need it some day.  */
177 #if DEFINE_FINI
178 #endif
179
180
181 /* If no arguments have to passed to the loop function define the macro
182    as empty.  */
183 #ifndef EXTRA_LOOP_ARGS
184 # define EXTRA_LOOP_ARGS
185 #endif
186
187
188 /* This is the actual conversion function.  */
189 #ifndef FUNCTION_NAME
190 # define FUNCTION_NAME  gconv
191 #endif
192
193 int
194 FUNCTION_NAME (struct gconv_step *step, struct gconv_step_data *data,
195                const char **inbuf, const char *inbufend, size_t *written,
196                int do_flush)
197 {
198   struct gconv_step *next_step = step + 1;
199   struct gconv_step_data *next_data = data + 1;
200   gconv_fct fct = next_step->fct;
201   int status;
202
203   /* If the function is called with no input this means we have to reset
204      to the initial state.  The possibly partly converted input is
205      dropped.  */
206   if (do_flush)
207     {
208       /* Call the steps down the chain if there are any.  */
209       if (data->is_last)
210         status = GCONV_OK;
211       else
212         {
213 #ifdef EMIT_SHIFT_TO_INIT
214           status = GCONV_OK;
215
216           EMIT_SHIFT_TO_INIT;
217
218           if (status == GCONV_OK)
219 #endif
220             /* Give the modules below the same chance.  */
221             status = (*fct) (next_step, next_data, NULL, NULL, written, 1);
222         }
223     }
224   else
225     {
226       /* We preserve the initial values of the pointer variables.  */
227       const char *inptr = *inbuf;
228       char *outbuf = data->outbuf;
229       char *outend = data->outbufend;
230       char *outptr;
231
232       /* This variable is used to count the number of characters we
233          actually converted.  */
234       size_t converted = 0;
235
236 #ifdef PREPARE_LOOP
237       PREPARE_LOOP
238 #endif
239
240       do
241         {
242           /* Remember the start value for this round.  */
243           inptr = *inbuf;
244           /* The outbuf buffer is empty.  */
245           outptr = outbuf;
246
247 #ifdef SAVE_RESET_STATE
248           SAVE_RESET_STATE (1);
249 #endif
250
251           if (FROM_DIRECTION)
252             /* Run the conversion loop.  */
253             status = FROM_LOOP ((const unsigned char **) inbuf,
254                                 (const unsigned char *) inbufend,
255                                 (unsigned char **) &outbuf,
256                                 (unsigned char *) outend,
257                                 data->statep, step->data, &converted
258                                 EXTRA_LOOP_ARGS);
259           else
260             /* Run the conversion loop.  */
261             status = TO_LOOP ((const unsigned char **) inbuf,
262                               (const unsigned char *) inbufend,
263                               (unsigned char **) &outbuf,
264                               (unsigned char *) outend,
265                               data->statep, step->data, &converted
266                               EXTRA_LOOP_ARGS);
267
268           /* If this is the last step leave the loop, there is nothgin
269              we can do.  */
270           if (data->is_last)
271             {
272               /* Store information about how many bytes are available.  */
273               data->outbuf = outbuf;
274
275               /* Remember how many characters we converted.  */
276               *written += converted;
277
278               break;
279             }
280
281           /* Write out all output which was produced.  */
282           if (outbuf > outptr)
283             {
284               const char *outerr = data->outbuf;
285               int result;
286
287               result = (*fct) (next_step, next_data, &outerr, outbuf,
288                                written, 0);
289
290               if (result != GCONV_EMPTY_INPUT)
291                 {
292                   if (outerr != outbuf)
293                     {
294 #ifdef RESET_INPUT_BUFFER
295                       RESET_INPUT_BUFFER;
296 #else
297                       /* We have a problem with the in on of the functions
298                          below.  Undo the conversion upto the error point.  */
299                       size_t nstatus;
300
301                       /* Reload the pointers.  */
302                       *inbuf = inptr;
303                       outbuf = outptr;
304
305                       /* Reset the state.  */
306 # ifdef SAVE_RESET_STATE
307                       SAVE_RESET_STATE (0);
308 # endif
309
310                       if (FROM_DIRECTION)
311                         /* Run the conversion loop.  */
312                         nstatus = FROM_LOOP ((const unsigned char **) inbuf,
313                                              (const unsigned char *) inbufend,
314                                              (unsigned char **) &outbuf,
315                                              (unsigned char *) outerr,
316                                              data->statep, step->data,
317                                              &converted EXTRA_LOOP_ARGS);
318                       else
319                         /* Run the conversion loop.  */
320                         nstatus = TO_LOOP ((const unsigned char **) inbuf,
321                                            (const unsigned char *) inbufend,
322                                            (unsigned char **) &outbuf,
323                                            (unsigned char *) outerr,
324                                            data->statep, step->data,
325                                            &converted EXTRA_LOOP_ARGS);
326
327                       /* We must run out of output buffer space in this
328                          rerun.  */
329                       assert (outbuf == outerr);
330                       assert (nstatus == GCONV_FULL_OUTPUT);
331 #endif  /* reset input buffer */
332                     }
333
334                   /* Change the status.  */
335                   status = result;
336                 }
337               else
338                 /* All the output is consumed, we can make another run
339                    if everything was ok.  */
340                 if (status == GCONV_FULL_OUTPUT)
341                   status = GCONV_OK;
342             }
343         }
344       while (status == GCONV_OK);
345
346 #ifdef END_LOOP
347       END_LOOP
348 #endif
349     }
350
351   return status;
352 }
353
354 #undef DEFINE_INIT
355 #undef CHARSET_NAME
356 #undef DEFINE_FINI
357 #undef MIN_NEEDED_FROM
358 #undef MIN_NEEDED_TO
359 #undef MAX_NEEDED_FROM
360 #undef MAX_NEEDED_TO
361 #undef DEFINE_DIRECTION_OBJECTS
362 #undef FROM_DIRECTION
363 #undef EMIT_SHIFT_TO_INIT
364 #undef FROM_LOOP
365 #undef TO_LOOP
366 #undef RESET_STATE
367 #undef RESET_INPUT_BUFFER
368 #undef FUNCTION_NAME
369 #undef PREPARE_LOOP
370 #undef END_LOOP