Reimplemented Unicode case mapping in a slightly more efficient way.
[wine] / unicode / wctomb.c
1 /*
2  * WideCharToMultiByte implementation
3  *
4  * Copyright 2000 Alexandre Julliard
5  */
6
7 #include <string.h>
8
9 #include "winnls.h"
10 #include "wine/unicode.h"
11
12 /* wcstombs for single-byte code page */
13 static inline int wcstombs_sbcs( const struct sbcs_table *table,
14                                  const WCHAR *src, unsigned int srclen,
15                                  char *dst, unsigned int dstlen )
16 {
17     const unsigned char  * const uni2cp_low = table->uni2cp_low;
18     const unsigned short * const uni2cp_high = table->uni2cp_high;
19     int ret = srclen;
20
21     if (dstlen < srclen)
22     {
23         /* buffer too small: fill it up to dstlen and return error */
24         srclen = dstlen;
25         ret = -1;
26     }
27
28     for (;;)
29     {
30         switch(srclen)
31         {
32         default:
33         case 16: dst[15] = uni2cp_low[uni2cp_high[src[15] >> 8] + (src[15] & 0xff)];
34         case 15: dst[14] = uni2cp_low[uni2cp_high[src[14] >> 8] + (src[14] & 0xff)];
35         case 14: dst[13] = uni2cp_low[uni2cp_high[src[13] >> 8] + (src[13] & 0xff)];
36         case 13: dst[12] = uni2cp_low[uni2cp_high[src[12] >> 8] + (src[12] & 0xff)];
37         case 12: dst[11] = uni2cp_low[uni2cp_high[src[11] >> 8] + (src[11] & 0xff)];
38         case 11: dst[10] = uni2cp_low[uni2cp_high[src[10] >> 8] + (src[10] & 0xff)];
39         case 10: dst[9]  = uni2cp_low[uni2cp_high[src[9]  >> 8] + (src[9]  & 0xff)];
40         case 9:  dst[8]  = uni2cp_low[uni2cp_high[src[8]  >> 8] + (src[8]  & 0xff)];
41         case 8:  dst[7]  = uni2cp_low[uni2cp_high[src[7]  >> 8] + (src[7]  & 0xff)];
42         case 7:  dst[6]  = uni2cp_low[uni2cp_high[src[6]  >> 8] + (src[6]  & 0xff)];
43         case 6:  dst[5]  = uni2cp_low[uni2cp_high[src[5]  >> 8] + (src[5]  & 0xff)];
44         case 5:  dst[4]  = uni2cp_low[uni2cp_high[src[4]  >> 8] + (src[4]  & 0xff)];
45         case 4:  dst[3]  = uni2cp_low[uni2cp_high[src[3]  >> 8] + (src[3]  & 0xff)];
46         case 3:  dst[2]  = uni2cp_low[uni2cp_high[src[2]  >> 8] + (src[2]  & 0xff)];
47         case 2:  dst[1]  = uni2cp_low[uni2cp_high[src[1]  >> 8] + (src[1]  & 0xff)];
48         case 1:  dst[0]  = uni2cp_low[uni2cp_high[src[0]  >> 8] + (src[0]  & 0xff)];
49         case 0: break;
50         }
51         if (srclen < 16) return ret;
52         dst += 16;
53         src += 16;
54         srclen -= 16;
55     }
56 }
57
58 /* slow version of wcstombs_sbcs that handles the various flags */
59 static int wcstombs_sbcs_slow( const struct sbcs_table *table, int flags,
60                                const WCHAR *src, unsigned int srclen,
61                                char *dst, unsigned int dstlen,
62                                const char *defchar, int *used )
63 {
64     const WCHAR * const cp2uni = table->cp2uni;
65     const unsigned char  * const uni2cp_low = table->uni2cp_low;
66     const unsigned short * const uni2cp_high = table->uni2cp_high;
67     const unsigned char table_default = table->info.def_char & 0xff;
68     int ret = srclen, tmp;
69
70     if (dstlen < srclen)
71     {
72         /* buffer too small: fill it up to dstlen and return error */
73         srclen = dstlen;
74         ret = -1;
75     }
76
77     if (!defchar) defchar = &table_default;
78     if (!used) used = &tmp;  /* avoid checking on every char */
79
80     while (srclen)
81     {
82         unsigned char ch = uni2cp_low[uni2cp_high[*src >> 8] + (*src & 0xff)];
83         if (((flags & WC_NO_BEST_FIT_CHARS) && (cp2uni[ch] != *src)) ||
84             (ch == table_default && *src != table->info.def_unicode_char))
85         {
86             ch = *defchar;
87             *used = 1;
88         }
89         *dst++ = ch;
90         src++;
91         srclen--;
92     }
93     return ret;
94 }
95
96 /* query necessary dst length for src string */
97 static inline int get_length_dbcs( const struct dbcs_table *table,
98                                    const WCHAR *src, unsigned int srclen )
99 {
100     const unsigned short * const uni2cp_low = table->uni2cp_low;
101     const unsigned short * const uni2cp_high = table->uni2cp_high;
102     int len;
103
104     for (len = 0; srclen; srclen--, src++, len++)
105     {
106         if (uni2cp_low[uni2cp_high[*src >> 8] + (*src & 0xff)] & 0xff00) len++;
107     }
108     return len;
109 }
110
111 /* wcstombs for double-byte code page */
112 static inline int wcstombs_dbcs( const struct dbcs_table *table,
113                                  const WCHAR *src, unsigned int srclen,
114                                  char *dst, unsigned int dstlen )
115 {
116     const unsigned short * const uni2cp_low = table->uni2cp_low;
117     const unsigned short * const uni2cp_high = table->uni2cp_high;
118     int len;
119
120     for (len = dstlen; srclen && len; len--, srclen--, src++)
121     {
122         unsigned short res = uni2cp_low[uni2cp_high[*src >> 8] + (*src & 0xff)];
123         if (res & 0xff00)
124         {
125             if (len == 1) break;  /* do not output a partial char */
126             len--;
127             *dst++ = res >> 8;
128         }
129         *dst++ = (char)res;
130     }
131     if (srclen) return -1;  /* overflow */
132     return dstlen - len;
133 }
134
135 /* slow version of wcstombs_dbcs that handles the various flags */
136 static int wcstombs_dbcs_slow( const struct dbcs_table *table, int flags,
137                                const WCHAR *src, unsigned int srclen,
138                                char *dst, unsigned int dstlen,
139                                const char *defchar, int *used )
140 {
141     const WCHAR * const cp2uni = table->cp2uni;
142     const unsigned short * const uni2cp_low = table->uni2cp_low;
143     const unsigned short * const uni2cp_high = table->uni2cp_high;
144     const unsigned char  * const cp2uni_lb = table->cp2uni_leadbytes;
145     WCHAR defchar_value = table->info.def_char;
146     int len, tmp;
147
148     if (defchar) defchar_value = defchar[1] ? ((defchar[0] << 8) | defchar[1]) : defchar[0];
149     if (!used) used = &tmp;  /* avoid checking on every char */
150
151     for (len = dstlen; srclen && len; len--, srclen--, src++)
152     {
153         unsigned short res = uni2cp_low[uni2cp_high[*src >> 8] + (*src & 0xff)];
154
155         if (res == table->info.def_char && *src != table->info.def_unicode_char)
156         {
157             res = defchar_value;
158             *used = 1;
159         }
160         else if (flags & WC_NO_BEST_FIT_CHARS)
161         {
162             /* check if char maps back to the same Unicode value */
163             if (res & 0xff00)
164             {
165                 unsigned char off = cp2uni_lb[res >> 8];
166                 if (cp2uni[(off << 8) + (res & 0xff)] != *src)
167                 {
168                     res = defchar_value;
169                     *used = 1;
170                 }
171             }
172             else if (cp2uni[res & 0xff] != *src)
173             {
174                 res = defchar_value;
175                 *used = 1;
176             }
177         }
178
179         if (res & 0xff00)
180         {
181             if (len == 1) break;  /* do not output a partial char */
182             len--;
183             *dst++ = res >> 8;
184         }
185         *dst++ = (char)res;
186     }
187     if (srclen) return -1;  /* overflow */
188     return dstlen - len;
189 }
190
191 /* wide char to multi byte string conversion */
192 /* return -1 on dst buffer overflow */
193 int cp_wcstombs( const union cptable *table, int flags,
194                  const WCHAR *src, int srclen,
195                  char *dst, int dstlen, const char *defchar, int *used )
196 {
197     if (table->info.char_size == 1)
198     {
199         if (!dstlen) return srclen;
200         if (flags || defchar || used)
201             return wcstombs_sbcs_slow( &table->sbcs, flags, src, srclen,
202                                        dst, dstlen, defchar, used );
203         return wcstombs_sbcs( &table->sbcs, src, srclen, dst, dstlen );
204     }
205     else /* mbcs */
206     {
207         if (!dstlen) return get_length_dbcs( &table->dbcs, src, srclen );
208         if (flags || defchar || used)
209             return wcstombs_dbcs_slow( &table->dbcs, flags, src, srclen,
210                                        dst, dstlen, defchar, used );
211         return wcstombs_sbcs( &table->sbcs, src, srclen, dst, dstlen );
212     }
213 }