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