]> mj.ucw.cz Git - libucw.git/blob - charset/charconv.c
An unfinished experiment in making a separate release of all libraries.
[libucw.git] / charset / charconv.c
1 /*
2  *      Character Set Conversion Library 1.2
3  *
4  *      (c) 1998--2004 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "charset/charconv.h"
12 #include "charset/chartable.h"
13
14 void
15 conv_init(struct conv_context *c)
16 {
17   c->source = c->source_end = NULL;
18   c->dest = c->dest_start = c->dest_end = NULL;
19 }
20
21 static int
22 conv_none(struct conv_context *c)
23 {
24   c->dest_start = (char *) c->source;
25   c->dest = (char *) c->source_end;
26   return CONV_SOURCE_END | CONV_DEST_END | CONV_SKIP;
27 }
28
29 enum state {
30   CLEAN,
31   SINGLE_WRITE,
32   SEQ_WRITE,
33   UTF8_READ,
34   UTF8_WRITE_START,
35   UTF8_WRITE_CONT
36 };
37
38 static int
39 conv_slow(struct conv_context *c)
40 {
41   const unsigned char *s = c->source;
42   const unsigned char *se = c->source_end;
43   unsigned char *d = c->dest;
44   unsigned char *de = c->dest_end;
45
46   switch (c->state)
47     {
48     case SINGLE_WRITE:
49       if (d >= de)
50         goto cde;
51       *d++ = c->code;
52       break;
53     case SEQ_WRITE:
54       while (c->remains)
55         {
56           if (d >= de)
57             goto cde;
58           *d++ = *c->string_at++;
59           c->remains--;
60         }
61       break;
62     case UTF8_READ:
63       while (c->remains)
64         {
65           if (s >= se)
66             goto cse;
67           if ((*s & 0xc0) != 0x80)
68             {
69               c->code = 0xfffd;
70               break;
71             }
72           c->code = (c->code << 6) | (*s++ & 0x3f);
73           c->remains--;
74         }
75       if (c->code >= 0x10000)
76         c->code = 0xfffd;
77       c->source = s;
78       c->state = 0;
79       return -1;
80     case UTF8_WRITE_START:
81       if (d >= de)
82         goto cde;
83       if (c->code < 0x80)
84         {
85           *d++ = c->code;
86           break;
87         }
88       else if (c->code < 0x800)
89         {
90           *d++ = 0xc0 | (c->code >> 6);
91           c->code <<= 10;
92           c->remains = 1;
93         }
94       else
95         {
96           *d++ = 0xe0 | (c->code >> 12);
97           c->code <<= 4;
98           c->remains = 2;
99         }
100       c->code &= 0xffff;
101       c->state = UTF8_WRITE_CONT;
102       /* fall-thru */
103     case UTF8_WRITE_CONT:
104       while (c->remains)
105         {
106           if (d >= de)
107             goto cde;
108           *d++ = 0x80 | (c->code >> 10);
109           c->code <<= 6;
110           c->remains--;
111         }
112       break;
113     default:
114       ASSERT(0);
115     }
116   c->source = s;
117   c->dest = d;
118   c->state = 0;
119   return 0;
120
121  cse:
122   c->source = s;
123   return CONV_SOURCE_END;
124
125  cde:
126   c->dest = d;
127   return CONV_DEST_END;
128 }
129
130 static int
131 conv_from_utf8(struct conv_context *c)
132 {
133   unsigned short *x_to_out = c->x_to_out;
134   const unsigned char *s, *se;
135   unsigned char *d, *de, *k;
136   unsigned int code, cc, len;
137   int e;
138
139   if (unlikely(c->state))
140     goto slow;
141
142 main:
143   s = c->source;
144   se = c->source_end;
145   d = c->dest;
146   de = c->dest_end;
147   while (s < se)                        /* Optimized for speed, beware of spaghetti code */
148     {
149       cc = *s++;
150       if (cc < 0x80)
151         code = cc;
152       else if (cc >= 0xc0)
153         {
154           if (s + 6 > se)
155             goto send_utf;
156           if (cc < 0xe0)
157             {
158               if ((s[0] & 0xc0) != 0x80)
159                 goto nocode;
160               code = cc & 0x1f;
161               code = (code << 6) | (*s++ & 0x3f);
162             }
163           else if (cc < 0xf0)
164             {
165               if ((s[0] & 0xc0) != 0x80 || (s[1] & 0xc0) != 0x80)
166                 goto nocode;
167               code = cc & 0x0f;
168               code = (code << 6) | (*s++ & 0x3f);
169               code = (code << 6) | (*s++ & 0x3f);
170             }
171           else if (cc < 0xfc)
172             {
173               while (cc & 0x80)
174                 {
175                   if ((*s++ & 0xc0) != 0x80)
176                     break;
177                   cc <<= 1;
178                 }
179               goto nocode;
180             }
181           else
182             goto nocode;
183         }
184       else
185         {
186         nocode:
187           code = 0xfffd;
188         }
189     got_code:
190       code = x_to_out[uni_to_x[code >> 8U][code & 0xff]];
191       if (code < 0x100)
192         {
193           if (d >= de)
194             goto dend_char;
195           *d++ = code;
196         }
197       else
198         {
199           k = string_table + code - 0x100;
200           len = *k++;
201           if (d + len > de)
202             goto dend_str;
203           while (len--)
204             *d++ = *k++;
205         }
206     }
207   c->source = s;
208   c->dest = d;
209   return CONV_SOURCE_END;
210
211 send_utf:
212   if (cc < 0xe0)                { c->code = cc & 0x1f; c->remains = 1; }
213   else if (cc < 0xf0)           { c->code = cc & 0x0f; c->remains = 2; }
214   else
215     {
216       c->code = ~0U;
217       if (cc < 0xf8)            c->remains = 3;
218       else if (cc < 0xfc)       c->remains = 4;
219       else if (cc < 0xfe)       c->remains = 5;
220       else goto nocode;
221     }
222   c->state = UTF8_READ;
223   goto go_slow;
224
225 dend_str:
226   c->state = SEQ_WRITE;
227   c->string_at = k;
228   c->remains = len;
229   goto go_slow;
230
231 dend_char:
232   c->state = SINGLE_WRITE;
233   c->code = code;
234 go_slow:
235   c->source = s;
236   c->dest = d;
237 slow:
238   e = conv_slow(c);
239   if (e < 0)
240     {
241       code = c->code;
242       s = c->source;
243       se = c->source_end;
244       d = c->dest;
245       de = c->dest_end;
246       goto got_code;
247     }
248   if (e)
249     return e;
250   goto main;
251 }
252
253 static int
254 conv_to_utf8(struct conv_context *c)
255 {
256   unsigned short *in_to_x = c->in_to_x;
257   const unsigned char *s, *se;
258   unsigned char *d, *de;
259   unsigned int code;
260   int e;
261
262   if (unlikely(c->state))
263     goto slow;
264
265 main:
266   s = c->source;
267   se = c->source_end;
268   d = c->dest;
269   de = c->dest_end;
270   while (s < se)
271     {
272       code = x_to_uni[in_to_x[*s]];
273       if (code < 0x80)
274         {
275           if (d >= de)
276             goto dend;
277           *d++ = code;
278         }
279       else if (code < 0x800)
280         {
281           if (d + 2 > de)
282             goto dend_utf;
283           *d++ = 0xc0 | (code >> 6);
284           *d++ = 0x80 | (code & 0x3f);
285         }
286       else
287         {
288           if (d + 3 > de)
289             goto dend_utf;
290           *d++ = 0xe0 | (code >> 12);
291           *d++ = 0x80 | ((code >> 6) & 0x3f);
292           *d++ = 0x80 | (code & 0x3f);
293         }
294       s++;
295     }
296   c->source = s;
297   c->dest = d;
298   return CONV_SOURCE_END;
299
300 dend:
301   c->source = s;
302   c->dest = d;
303   return CONV_DEST_END;
304
305 dend_utf:
306   c->source = s+1;
307   c->dest = d;
308   c->state = UTF8_WRITE_START;
309   c->code = code;
310 slow:
311   e = conv_slow(c);
312   if (e)
313     return e;
314   goto main;
315 }
316
317 static int
318 conv_standard(struct conv_context *c)
319 {
320   unsigned short *in_to_x = c->in_to_x;
321   unsigned short *x_to_out = c->x_to_out;
322   const unsigned char *s, *se;
323   unsigned char *d, *de, *k;
324   unsigned int len, e;
325
326   if (unlikely(c->state))
327     goto slow;
328
329 main:
330   s = c->source;
331   se = c->source_end;
332   d = c->dest;
333   de = c->dest_end;
334   while (s < se)
335     {
336       unsigned int code = x_to_out[in_to_x[*s]];
337       if (code < 0x100)
338         {
339           if (unlikely(d >= de))
340             goto dend;
341           *d++ = code;
342         }
343       else
344         {
345           k = string_table + code - 0x100;
346           len = *k++;
347           if (unlikely(d + len > de))
348             goto dend_str;
349           while (len--)
350             *d++ = *k++;
351         }
352       s++;
353     }
354   c->source = s;
355   c->dest = d;
356   return CONV_SOURCE_END;
357
358 dend:
359   c->source = s;
360   c->dest = d;
361   return CONV_DEST_END;
362
363 dend_str:
364   c->source = s;
365   c->dest = d;
366   c->state = SEQ_WRITE;
367   c->string_at = k;
368   c->remains = len;
369 slow:
370   e = conv_slow(c);
371   if (e)
372     return e;
373   goto main;
374 }
375
376 void
377 conv_set_charset(struct conv_context *c, int src, int dest)
378 {
379   c->source_charset = src;
380   c->dest_charset = dest;
381   if (src == dest)
382     c->convert = conv_none;
383   else
384     {
385       c->convert = conv_standard;
386       if (src == CONV_CHARSET_UTF8)
387         c->convert = conv_from_utf8;
388       else
389         c->in_to_x = input_to_x[src];
390       if (dest == CONV_CHARSET_UTF8)
391         c->convert = conv_to_utf8;
392       else
393         c->x_to_out = x_to_output[dest];
394     }
395   c->state = 0;
396 }
397
398 unsigned int
399 conv_x_to_ucs(unsigned int x)
400 {
401   return x_to_uni[x];
402 }
403
404 unsigned int
405 conv_ucs_to_x(unsigned int ucs)
406 {
407   return uni_to_x[ucs >> 8U][ucs & 0xff];
408 }
409
410 unsigned int
411 conv_x_count(void)
412 {
413   return sizeof(x_to_uni) / sizeof(x_to_uni[0]);
414 }
415
416 int
417 conv_in_to_ucs(struct conv_context *c, unsigned int y)
418 {
419   return x_to_uni[c->in_to_x[y]];
420 }
421
422 int conv_ucs_to_out(struct conv_context *c, unsigned int ucs)
423 {
424   uns x = uni_to_x[ucs >> 8U][ucs & 0xff];
425   if (x == 256 || c->x_to_out[x] >= 256)
426     return -1;
427   else
428     return c->x_to_out[x];
429 }