]> mj.ucw.cz Git - libucw.git/blob - lib/unicode.h
noted that utf8_{put/get} supports only a subset of the Unicode range
[libucw.git] / lib / unicode.h
1 /*
2  *      UCW Library -- Unicode Characters
3  *
4  *      (c) 1997--2004 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 #ifndef _UCW_UNICODE_H
12 #define _UCW_UNICODE_H
13
14 /* Macros for handling UTF-8 */
15
16 #define UNI_REPLACEMENT 0xfffc
17
18 /* Encode a character from the basic character set [0, 0xFFFF]
19  * (subset of the formal Unicode range); up to 3 bytes needed (RFC2279) */
20 static inline byte *
21 utf8_put(byte *p, uns u)
22 {
23   if (u < 0x80)
24     *p++ = u;
25   else if (u < 0x800)
26     {
27       *p++ = 0xc0 | (u >> 6);
28       *p++ = 0x80 | (u & 0x3f);
29     }
30   else
31     {
32       ASSERT(u < 0x10000);
33       *p++ = 0xe0 | (u >> 12);
34       *p++ = 0x80 | ((u >> 6) & 0x3f);
35       *p++ = 0x80 | (u & 0x3f);
36     }
37   return p;
38 }
39
40 /* Encode a value from the range [0, 0x7FFFFFFF]; up to 6 bytes needed (RFC2279) */
41 static inline byte *
42 utf8_32_put(byte *p, uns u)
43 {
44   if (u < 0x80)
45     *p++ = u;
46   else if (u < 0x800)
47     {
48       *p++ = 0xc0 | (u >> 6);
49       goto put1;
50     }
51   else if (u < (1<<16))
52     {
53       *p++ = 0xe0 | (u >> 12);
54       goto put2;
55     }
56   else if (u < (1<<21))
57     {
58       *p++ = 0xf0 | (u >> 18);
59       goto put3;
60     }
61   else if (u < (1<<26))
62     {
63       *p++ = 0xf8 | (u >> 24);
64       goto put4;
65     }
66   else if (u < (1U<<31))
67     {
68       *p++ = 0xfc | (u >> 30);
69       *p++ = 0x80 | ((u >> 24) & 0x3f);
70 put4: *p++ = 0x80 | ((u >> 18) & 0x3f);
71 put3: *p++ = 0x80 | ((u >> 12) & 0x3f);
72 put2: *p++ = 0x80 | ((u >> 6) & 0x3f);
73 put1: *p++ = 0x80 | (u & 0x3f);
74     }
75   else
76     ASSERT(0);
77   return p;
78 }
79
80 #define UTF8_GET_NEXT if (unlikely((*p & 0xc0) != 0x80)) goto bad; u = (u << 6) | (*p++ & 0x3f)
81
82 /* Decode a character from the basic character set [0, 0xFFFF]
83  * or return UNI_REPLACEMENT if the encoding has been corrupted */
84 static inline byte *
85 utf8_get(const byte *p, uns *uu)
86 {
87   uns u = *p++;
88   if (u < 0x80)
89     ;
90   else if (unlikely(u < 0xc0))
91     {
92       /* Incorrect byte sequence */
93     bad:
94       u = UNI_REPLACEMENT;
95     }
96   else if (u < 0xe0)
97     {
98       u &= 0x1f;
99       UTF8_GET_NEXT;
100     }
101   else if (likely(u < 0xf0))
102     {
103       u &= 0x0f;
104       UTF8_GET_NEXT;
105       UTF8_GET_NEXT;
106     }
107   else
108     goto bad;
109   *uu = u;
110   return (byte *)p;
111 }
112
113 /* Decode a value from the range [0, 0x7FFFFFFF] 
114  * or return UNI_REPLACEMENT if the encoding has been corrupted */
115 static inline byte *
116 utf8_32_get(const byte *p, uns *uu)
117 {
118   uns u = *p++;
119   if (u < 0x80)
120     ;
121   else if (unlikely(u < 0xc0))
122     {
123       /* Incorrect byte sequence */
124     bad:
125       u = UNI_REPLACEMENT;
126     }
127   else if (u < 0xe0)
128     {
129       u &= 0x1f;
130       goto get1;
131     }
132   else if (u < 0xf0)
133     {
134       u &= 0x0f;
135       goto get2;
136     }
137   else if (u < 0xf8)
138     {
139       u &= 0x07;
140       goto get3;
141     }
142   else if (u < 0xfc)
143     {
144       u &= 0x03;
145       goto get4;
146     }
147   else if (u < 0xfe)
148     {
149       u &= 0x01;
150       UTF8_GET_NEXT;
151 get4: UTF8_GET_NEXT;
152 get3: UTF8_GET_NEXT;
153 get2: UTF8_GET_NEXT;
154 get1: UTF8_GET_NEXT;
155     }
156   else
157     goto bad;
158   *uu = u;
159   return (byte *)p;
160 }
161
162 #define PUT_UTF8(p,u) p = utf8_put(p, u)
163 #define GET_UTF8(p,u) p = (byte*)utf8_get(p, &(u))
164
165 #define PUT_UTF8_32(p,u) p = utf8_32_put(p, u)
166 #define GET_UTF8_32(p,u) p = (byte*)utf8_32_get(p, &(u))
167
168 #define UTF8_SKIP(p) do {                               \
169     uns c = *p++;                                       \
170     if (c >= 0xc0)                                      \
171       while (c & 0x40 && *p >= 0x80 && *p < 0xc0)       \
172         p++, c <<= 1;                                   \
173   } while (0)
174
175 #define UTF8_SKIP_BWD(p) while ((*--(p) & 0xc0) == 0x80)
176
177 static inline uns
178 utf8_space(uns u)
179 {
180   if (u < 0x80)
181     return 1;
182   if (u < 0x800)
183     return 2;
184   if (u < (1<<16))
185     return 3;
186   if (u < (1<<21))
187     return 4;
188   if (u < (1<<26))
189     return 5;
190   return 6;
191 }
192
193 static inline uns
194 utf8_encoding_len(uns c)
195 {
196   if (c < 0x80)
197     return 1;
198   ASSERT(c >= 0xc0 && c < 0xfe);
199   if (c < 0xe0)
200     return 2;
201   if (c < 0xf0)
202     return 3;
203   if (c < 0xf8)
204     return 4;
205   if (c < 0xfc)
206     return 5;
207   return 6;
208 }
209
210 /* unicode-utf8.c */
211
212 uns utf8_strlen(const byte *str);
213 uns utf8_strnlen(const byte *str, uns n);
214 uns utf8_check(const byte *str);
215
216 #endif