r8520: fixed a pile of warnings from the build farm gcc -Wall output on
[mat/samba.git] / source4 / torture / local / iconv.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    local testing of iconv routines. This tests the system iconv code against
5    the built-in iconv code
6
7    Copyright (C) Andrew Tridgell 2004
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "system/iconv.h"
26 #include "system/time.h"
27
28 #if HAVE_NATIVE_ICONV
29 /*
30   generate a UTF-16LE buffer for a given unicode codepoint
31 */
32 static int gen_codepoint_utf16(unsigned int codepoint,
33                                char *buf, size_t *size)
34 {
35         static iconv_t cd;
36         uint8_t in[4];
37         char *ptr_in;
38         size_t size_in, size_out, ret;
39         if (!cd) {
40                 cd = iconv_open("UTF-16LE", "UCS-4LE");
41                 if (cd == (iconv_t)-1) {
42                         cd = NULL;
43                         return -1;
44                 }
45         }
46
47         in[0] = codepoint & 0xFF;
48         in[1] = (codepoint>>8) & 0xFF;
49         in[2] = (codepoint>>16) & 0xFF;
50         in[3] = (codepoint>>24) & 0xFF;
51
52         ptr_in = in;
53         size_in = 4;
54         size_out = 8;
55
56         ret = iconv(cd, &ptr_in, &size_in, &buf, &size_out);
57
58         *size = 8 - size_out;
59
60         return ret;
61 }
62
63
64 /*
65   work out the unicode codepoint of the first UTF-8 character in the buffer
66 */
67 static unsigned int get_codepoint(char *buf, size_t size, const char *charset)
68 {
69         iconv_t cd;
70         uint8_t out[4];
71         char *ptr_out;
72         size_t size_out, size_in, ret;
73
74         cd = iconv_open("UCS-4LE", charset);
75
76         size_in = size;
77         ptr_out = out;
78         size_out = sizeof(out);
79         memset(out, 0, sizeof(out));
80
81         ret = iconv(cd, &buf, &size_in, &ptr_out, &size_out);
82
83         iconv_close(cd);
84
85         return out[0] | (out[1]<<8) | (out[2]<<16) | (out[3]<<24);
86 }
87
88 /*
89   display a buffer with name prefix
90 */
91 static void show_buf(const char *name, uint8_t *buf, size_t size)
92 {
93         int i;
94         printf("%s ", name);
95         for (i=0;i<size;i++) {
96                 printf("%02x ", buf[i]);
97         }
98         printf("\n");
99 }
100
101 /*
102   given a UTF-16LE buffer, test the system and built-in iconv code to
103   make sure they do exactly the same thing in converting the buffer to
104   "charset", then convert it back again and ensure we get the same
105   buffer back
106 */
107 static int test_buffer(uint8_t *inbuf, size_t size, const char *charset)
108 {
109         uint8_t buf1[1000], buf2[1000], buf3[1000];
110         size_t outsize1, outsize2, outsize3;
111         const char *ptr_in;
112         char *ptr_out;
113         size_t size_in1, size_in2, size_in3;
114         size_t ret1, ret2, ret3, len1, len2;
115         int ok = 1;
116         int errno1, errno2;
117         static iconv_t cd;
118         static smb_iconv_t cd2, cd3;
119         static const char *last_charset;
120
121         if (cd && last_charset) {
122                 iconv_close(cd);
123                 smb_iconv_close(cd2);
124                 smb_iconv_close(cd3);
125                 cd = NULL;
126         }
127
128         if (!cd) {
129                 cd = iconv_open(charset, "UTF-16LE");
130                 if (cd == (iconv_t)-1) {
131                         cd = NULL;
132                         return 0;
133                 }
134                 cd2 = smb_iconv_open(charset, "UTF-16LE");
135                 cd3 = smb_iconv_open("UTF-16LE", charset);
136                 last_charset = charset;
137         }
138
139         /* internal convert to charset - placing result in buf1 */
140         ptr_in = inbuf;
141         ptr_out = buf1;
142         size_in1 = size;
143         outsize1 = sizeof(buf1);
144
145         memset(ptr_out, 0, outsize1);
146         errno = 0;
147         ret1 = smb_iconv(cd2, &ptr_in, &size_in1, &ptr_out, &outsize1);
148         errno1 = errno;
149
150         /* system convert to charset - placing result in buf2 */
151         ptr_in = inbuf;
152         ptr_out = buf2;
153         size_in2 = size;
154         outsize2 = sizeof(buf2);
155         
156         memset(ptr_out, 0, outsize2);
157         errno = 0;
158         ret2 = iconv(cd, discard_const_p(char *, &ptr_in), &size_in2, &ptr_out, &outsize2);
159         errno2 = errno;
160
161         len1 = sizeof(buf1) - outsize1;
162         len2 = sizeof(buf2) - outsize2;
163
164         /* codepoints above 1M are not interesting for now */
165         if (len2 > len1 && 
166             memcmp(buf1, buf2, len1) == 0 && 
167             get_codepoint(buf2+len1, len2-len1, charset) >= (1<<20)) {
168                 return ok;
169         }
170         if (len1 > len2 && 
171             memcmp(buf1, buf2, len2) == 0 && 
172             get_codepoint(buf1+len2, len1-len2, charset) >= (1<<20)) {
173                 return ok;
174         }
175
176         if (ret1 != ret2) {
177                 printf("ret1=%d ret2=%d\n", (int)ret1, (int)ret2);
178                 ok = 0;
179         }
180
181         if (errno1 != errno2) {
182                 printf("e1=%s e2=%s\n", strerror(errno1), strerror(errno2));
183                 show_buf(" rem1:", inbuf+(size-size_in1), size_in1);
184                 show_buf(" rem2:", inbuf+(size-size_in2), size_in2);
185                 ok = 0;
186         }
187         
188         if (outsize1 != outsize2) {
189                 printf("\noutsize mismatch outsize1=%d outsize2=%d\n",
190                        (int)outsize1, (int)outsize2);
191                 ok = 0;
192         }
193         
194         if (size_in1 != size_in2) {
195                 printf("\nsize_in mismatch size_in1=%d size_in2=%d\n",
196                        (int)size_in1, (int)size_in2);
197                 ok = 0;
198         }
199
200         if (!ok ||
201             len1 != len2 ||
202             memcmp(buf1, buf2, len1) != 0) {
203                 printf("\nsize=%d ret1=%d ret2=%d\n", (int)size, (int)ret1, (int)ret2);
204                 show_buf(" IN1:", inbuf, size-size_in1);
205                 show_buf(" IN2:", inbuf, size-size_in2);
206                 show_buf("OUT1:", buf1, len1);
207                 show_buf("OUT2:", buf2, len2);
208                 if (len2 > len1 && memcmp(buf1, buf2, len1) == 0) {
209                         printf("next codepoint is %u\n", 
210                                get_codepoint(buf2+len1, len2-len1, charset));
211                 }
212                 if (len1 > len2 && memcmp(buf1, buf2, len2) == 0) {
213                         printf("next codepoint is %u\n", 
214                                get_codepoint(buf1+len2,len1-len2, charset));
215                 }
216
217                 ok = 0;
218         }
219
220         /* convert back to UTF-16, putting result in buf3 */
221         size = size - size_in1;
222         ptr_in = buf1;
223         ptr_out = buf3;
224         size_in3 = len1;
225         outsize3 = sizeof(buf3);
226
227         memset(ptr_out, 0, outsize3);
228         ret3 = smb_iconv(cd3, &ptr_in, &size_in3, &ptr_out, &outsize3);
229
230         /* we only internally support the first 1M codepoints */
231         if (outsize3 != sizeof(buf3) - size &&
232             get_codepoint(inbuf+sizeof(buf3) - outsize3, 
233                           size - (sizeof(buf3) - outsize3),
234                           "UTF-16LE") >= (1<<20)) {
235                 return ok;
236         }
237
238         if (ret3 != 0) {
239                 printf("pull failed - %s\n", strerror(errno));
240                 ok = 0;
241         }
242
243         if (strncmp(charset, "UTF", 3) != 0) {
244                 /* don't expect perfect mappings for non UTF charsets */
245                 return ok;
246         }
247
248
249         if (outsize3 != sizeof(buf3) - size) {
250                 printf("wrong outsize3 - %d should be %d\n", 
251                        (int)outsize3, (int)(sizeof(buf3) - size));
252                 ok = 0;
253         }
254         
255         if (memcmp(buf3, inbuf, size) != 0) {
256                 printf("pull bytes mismatch:\n");
257                 show_buf("inbuf", inbuf, size);
258                 show_buf(" buf3", buf3, sizeof(buf3) - outsize3);
259                 ok = 0;
260                 printf("next codepoint is %u\n", 
261                        get_codepoint(inbuf+sizeof(buf3) - outsize3, 
262                                      size - (sizeof(buf3) - outsize3),
263                                      "UTF-16LE"));
264         }
265
266         if (!ok) {
267                 printf("test_buffer failed for charset %s\n", charset);
268         }
269
270         return ok;
271 }
272
273
274 /*
275   test the push_codepoint() and next_codepoint() functions for a given
276   codepoint
277 */
278 static int test_codepoint(unsigned int codepoint)
279 {
280         uint8_t buf[10];
281         size_t size, size2;
282         codepoint_t c;
283
284         size = push_codepoint(buf, codepoint);
285         if (size == -1) {
286                 if (codepoint < 0xd800 || codepoint > 0x10000) {
287                         return 0;
288                 }
289                 return 1;
290         }
291         buf[size] = random();
292         buf[size+1] = random();
293         buf[size+2] = random();
294         buf[size+3] = random();
295
296         c = next_codepoint(buf, &size2);
297
298         if (c != codepoint) {
299                 printf("next_codepoint(%u) failed - gave %u\n", codepoint, c);
300                 return 0;
301         }
302
303         if (size2 != size) {
304                 printf("next_codepoint(%u) gave wrong size %d (should be %d)\n", 
305                        codepoint, (int)size2, (int)size);
306                 return 0;
307         }
308
309         return 1;
310 }
311
312 BOOL torture_local_iconv(void) 
313 {
314         size_t size;
315         unsigned char inbuf[1000];
316         int ok = 1;
317         unsigned int codepoint, i, c;
318         static iconv_t cd;
319
320         srandom(time(NULL));
321
322         cd = iconv_open("UTF-16LE", "UCS-4LE");
323         if (cd == (iconv_t)-1) {
324                 printf("unable to test - system iconv library does not support UTF-16LE -> UCS-4LE\n");
325                 return True;
326         }
327         iconv_close(cd);
328
329         printf("Testing next_codepoint()\n");
330         for (codepoint=0;ok && codepoint<(1<<20);codepoint++) {
331                 ok = test_codepoint(codepoint);
332         }
333
334         printf("Testing first 1M codepoints\n");
335         for (codepoint=0;ok && codepoint<(1<<20);codepoint++) {
336                 if (gen_codepoint_utf16(codepoint, inbuf, &size) != 0) {
337                         continue;
338                 }
339
340                 if (codepoint % 1000 == 0) {
341                         if (!lp_parm_bool(-1, "torture", "progress", True)) {
342                                 printf("codepoint=%u   \r", codepoint);
343                         }
344                 }
345
346                 ok = test_buffer(inbuf, size, "UTF-8");
347         }
348
349
350         printf("Testing 5M random UTF-16LE sequences\n");
351         for (i=0;ok && i<500000;i++) {
352                 if (i % 1000 == 0) {
353                         if (!lp_parm_bool(-1, "torture", "progress", True)) {
354                                 printf("i=%u              \r", i);
355                         }
356                 }
357
358                 size = random() % 100;
359                 for (c=0;c<size;c++) {
360                         if (random() % 100 < 80) {
361                                 inbuf[c] = random() % 128;
362                         } else {
363                                 inbuf[c] = random();
364                         }
365                         if (random() % 10 == 0) {
366                                 inbuf[c] |= 0xd8;
367                         }
368                         if (random() % 10 == 0) {
369                                 inbuf[c] |= 0xdc;
370                         }
371                 }
372                 ok &= test_buffer(inbuf, size, "UTF-8");
373                 ok &= test_buffer(inbuf, size, "CP850");
374         }
375
376         return ok == 1;
377 }
378
379
380 #else
381
382 BOOL torture_local_iconv(void) 
383 {
384         printf("No native iconv library - can't run iconv test\n");
385         return True;
386 }
387
388 #endif