]> mj.ucw.cz Git - libucw.git/blob - ucw/ff-unicode.c
Released as 6.5.13.
[libucw.git] / ucw / ff-unicode.c
1 /*
2  *      UCW Library: Reading and writing of UTF-8 on Fastbuf Streams
3  *
4  *      (c) 2001--2015 Martin Mares <mj@ucw.cz>
5  *      (c) 2004 Robert Spalek <robert@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 <ucw/lib.h>
12 #include <ucw/fastbuf.h>
13 #include <ucw/unicode.h>
14 #include <ucw/ff-unicode.h>
15 #include <ucw/ff-binary.h>
16
17 /*** UTF-8 ***/
18
19 int
20 bget_utf8_slow(struct fastbuf *b, uint repl)
21 {
22   int c = bgetc(b);
23   int code;
24
25   if (c < 0x80)                         /* Includes EOF */
26     return c;
27   if (c < 0xc0)                         /* Incorrect combination */
28     return repl;
29   if (c >= 0xf0)                        /* Too large, skip it */
30     {
31       while ((c = bgetc(b)) >= 0x80 && c < 0xc0)
32         ;
33       goto wrong;
34     }
35   if (c >= 0xe0)                        /* 3 bytes */
36     {
37       code = c & 0x0f;
38       if ((c = bgetc(b)) < 0x80 || c >= 0xc0)
39         goto wrong;
40       code = (code << 6) | (c & 0x3f);
41       if ((c = bgetc(b)) < 0x80 || c >= 0xc0)
42         goto wrong;
43       code = (code << 6) | (c & 0x3f);
44       if (code < 0x800)
45         goto wrong2;
46     }
47   else                                  /* 2 bytes */
48     {
49       code = c & 0x1f;
50       if ((c = bgetc(b)) < 0x80 || c >= 0xc0)
51         goto wrong;
52       code = (code << 6) | (c & 0x3f);
53       if (code < 0x80)
54         goto wrong2;
55     }
56   return code;
57
58 wrong:
59   if (c >= 0)
60     bungetc(b);
61 wrong2:
62   return repl;
63 }
64
65 int
66 bget_utf8_32_slow(struct fastbuf *b, uint repl)
67 {
68   int c = bgetc(b);
69   int code;
70   int nr;
71   int limit;
72
73   if (c < 0x80)                         /* Includes EOF */
74     return c;
75   if (c < 0xc0)                         /* Incorrect combination */
76     return repl;
77   if (c < 0xe0)
78     {
79       code = c & 0x1f;
80       nr = 1;
81       limit = 0x80;
82     }
83   else if (c < 0xf0)
84     {
85       code = c & 0x0f;
86       nr = 2;
87       limit = 0x800;
88     }
89   else if (c < 0xf8)
90     {
91       code = c & 0x07;
92       nr = 3;
93       limit = 1 << 16;
94     }
95   else if (c < 0xfc)
96     {
97       code = c & 0x03;
98       nr = 4;
99       limit = 1 << 21;
100     }
101   else if (c < 0xfe)
102     {
103       code = c & 0x01;
104       nr = 5;
105       limit = 1 << 26;
106     }
107   else                                  /* Too large */
108     goto wrong2;
109   while (nr-- > 0)
110     {
111       if ((c = bgetc(b)) < 0x80 || c >= 0xc0)
112         goto wrong;
113       code = (code << 6) | (c & 0x3f);
114     }
115   if (code < limit)
116     goto wrong2;
117   return code;
118
119 wrong:
120   if (c >= 0)
121     bungetc(b);
122 wrong2:
123   return repl;
124 }
125
126 void
127 bput_utf8_slow(struct fastbuf *b, uint u)
128 {
129   ASSERT(u < 65536);
130   if (u < 0x80)
131     bputc(b, u);
132   else
133     {
134       if (u < 0x800)
135         bputc(b, 0xc0 | (u >> 6));
136       else
137         {
138           bputc(b, 0xe0 | (u >> 12));
139           bputc(b, 0x80 | ((u >> 6) & 0x3f));
140         }
141       bputc(b, 0x80 | (u & 0x3f));
142     }
143 }
144
145 void
146 bput_utf8_32_slow(struct fastbuf *b, uint u)
147 {
148   ASSERT(u < (1U<<31));
149   if (u < 0x80)
150     bputc(b, u);
151   else
152     {
153       if (u < 0x800)
154         bputc(b, 0xc0 | (u >> 6));
155       else
156         {
157           if (u < (1<<16))
158             bputc(b, 0xe0 | (u >> 12));
159           else
160             {
161               if (u < (1<<21))
162                 bputc(b, 0xf0 | (u >> 18));
163               else
164                 {
165                   if (u < (1<<26))
166                     bputc(b, 0xf8 | (u >> 24));
167                   else
168                     {
169                       bputc(b, 0xfc | (u >> 30));
170                       bputc(b, 0x80 | ((u >> 24) & 0x3f));
171                     }
172                   bputc(b, 0x80 | ((u >> 18) & 0x3f));
173                 }
174               bputc(b, 0x80 | ((u >> 12) & 0x3f));
175             }
176           bputc(b, 0x80 | ((u >> 6) & 0x3f));
177         }
178       bputc(b, 0x80 | (u & 0x3f));
179     }
180 }
181
182 /*** UTF-16 ***/
183
184 int
185 bget_utf16_be_slow(struct fastbuf *b, uint repl)
186 {
187   if (bpeekc(b) < 0)
188     return -1;
189   uint u = bgetw_be(b), x, y;
190   if ((int)u < 0)
191     return repl;
192   if ((x = u - 0xd800) >= 0x800)
193     return u;
194   if (x >= 0x400 || bpeekc(b) < 0 || (y = bgetw_be(b) - 0xdc00) >= 0x400)
195     return repl;
196   return 0x10000 + (x << 10) + y;
197 }
198
199 int
200 bget_utf16_le_slow(struct fastbuf *b, uint repl)
201 {
202   if (bpeekc(b) < 0)
203     return -1;
204   uint u = bgetw_le(b), x, y;
205   if ((int)u < 0)
206     return repl;
207   if ((x = u - 0xd800) >= 0x800)
208     return u;
209   if (x >= 0x400 || bpeekc(b) < 0 || (y = bgetw_le(b) - 0xdc00) >= 0x400)
210     return repl;
211   return 0x10000 + (x << 10) + y;
212 }
213
214 void
215 bput_utf16_be_slow(struct fastbuf *b, uint u)
216 {
217   if (u < 0xd800 || (u < 0x10000 && u >= 0xe000))
218     {
219       bputc(b, u >> 8);
220       bputc(b, u & 0xff);
221     }
222   else if ((u -= 0x10000) < 0x100000)
223     {
224       bputc(b, 0xd8 | (u >> 18));
225       bputc(b, (u >> 10) & 0xff);
226       bputc(b, 0xdc | ((u >> 8) & 0x3));
227       bputc(b, u & 0xff);
228     }
229   else
230     ASSERT(0);
231 }
232
233 void
234 bput_utf16_le_slow(struct fastbuf *b, uint u)
235 {
236   if (u < 0xd800 || (u < 0x10000 && u >= 0xe000))
237     {
238       bputc(b, u & 0xff);
239       bputc(b, u >> 8);
240     }
241   else if ((u -= 0x10000) < 0x100000)
242     {
243       bputc(b, (u >> 10) & 0xff);
244       bputc(b, 0xd8 | (u >> 18));
245       bputc(b, u & 0xff);
246       bputc(b, 0xdc | ((u >> 8) & 0x3));
247     }
248   else
249     ASSERT(0);
250 }
251
252 #ifdef TEST
253
254 #include <stdlib.h>
255 #include <stdio.h>
256
257 int main(int argc, char **argv)
258 {
259 #define FUNCS \
260   F(BGET_UTF8) F(BGET_UTF8_32) F(BGET_UTF16_BE) F(BGET_UTF16_LE) \
261   F(BPUT_UTF8) F(BPUT_UTF8_32) F(BPUT_UTF16_BE) F(BPUT_UTF16_LE)
262
263   enum {
264 #define F(x) FUNC_##x,
265     FUNCS
266 #undef F
267   };
268   char *names[] = {
269 #define F(x) [FUNC_##x] = #x,
270     FUNCS
271 #undef F
272   };
273
274   uint func = ~0U;
275   if (argc > 1)
276     for (uint i = 0; i < ARRAY_SIZE(names); i++)
277       if (!strcasecmp(names[i], argv[1]))
278         func = i;
279   if (!~func)
280     {
281       fprintf(stderr, "Invalid usage!\n");
282       return 1;
283     }
284
285   struct fastbuf *b = fbgrow_create(8);
286   if (func < FUNC_BPUT_UTF8)
287     {
288       uint u;
289       while (scanf("%x", &u) == 1)
290         bputc(b, u);
291       fbgrow_rewind(b);
292       while (bpeekc(b) >= 0)
293         {
294           if (btell(b))
295             putchar(' ');
296           switch (func)
297             {
298               case FUNC_BGET_UTF8:
299                 u = bget_utf8_slow(b, UNI_REPLACEMENT);
300                 break;
301               case FUNC_BGET_UTF8_32:
302                 u = bget_utf8_32_slow(b, UNI_REPLACEMENT);
303                 break;
304               case FUNC_BGET_UTF16_BE:
305                 u = bget_utf16_be_slow(b, UNI_REPLACEMENT);
306                 break;
307               case FUNC_BGET_UTF16_LE:
308                 u = bget_utf16_le_slow(b, UNI_REPLACEMENT);
309                 break;
310               default:
311                 ASSERT(0);
312             }
313           printf("%04x", u);
314         }
315       putchar('\n');
316     }
317   else
318     {
319       uint u, i = 0;
320       while (scanf("%x", &u) == 1)
321         {
322           switch (func)
323             {
324               case FUNC_BPUT_UTF8:
325                 bput_utf8_slow(b, u);
326                 break;
327               case FUNC_BPUT_UTF8_32:
328                 bput_utf8_32_slow(b, u);
329                 break;
330               case FUNC_BPUT_UTF16_BE:
331                 bput_utf16_be_slow(b, u);
332                 break;
333               case FUNC_BPUT_UTF16_LE:
334                 bput_utf16_le_slow(b, u);
335                 break;
336               default:
337                 ASSERT(0);
338             }
339           fbgrow_rewind(b);
340           u = 0;
341           while (bpeekc(b) >= 0)
342             {
343               if (i++)
344                 putchar(' ');
345               printf("%02x", bgetc(b));
346             }
347           fbgrow_reset(b);
348         }
349       putchar('\n');
350     }
351   bclose(b);
352
353   return 0;
354 }
355
356 #endif