gkdbus: Fix underflow and unreachable code bug
[platform/upstream/glib.git] / gio / tests / converter-stream.c
1 /* GLib testing framework examples and tests
2  * Copyright (C) 2009 Red Hat, Inc.
3  * Authors: Alexander Larsson <alexl@redhat.com>
4  *
5  * SPDX-License-Identifier: LicenseRef-old-glib-tests
6  *
7  * This work is provided "as is"; redistribution and modification
8  * in whole or in part, in any medium, physical or electronic is
9  * permitted without restriction.
10  *
11  * This work is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  * In no event shall the authors or contributors be liable for any
16  * direct, indirect, incidental, special, exemplary, or consequential
17  * damages (including, but not limited to, procurement of substitute
18  * goods or services; loss of use, data, or profits; or business
19  * interruption) however caused and on any theory of liability, whether
20  * in contract, strict liability, or tort (including negligence or
21  * otherwise) arising in any way out of the use of this software, even
22  * if advised of the possibility of such damage.
23  */
24
25 #include <glib/glib.h>
26 #include <gio/gio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #define G_TYPE_EXPANDER_CONVERTER         (g_expander_converter_get_type ())
31 #define G_EXPANDER_CONVERTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_EXPANDER_CONVERTER, GExpanderConverter))
32 #define G_EXPANDER_CONVERTER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_EXPANDER_CONVERTER, GExpanderConverterClass))
33 #define G_IS_EXPANDER_CONVERTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_EXPANDER_CONVERTER))
34 #define G_IS_EXPANDER_CONVERTER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_EXPANDER_CONVERTER))
35 #define G_EXPANDER_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_EXPANDER_CONVERTER, GExpanderConverterClass))
36
37 typedef struct _GExpanderConverter       GExpanderConverter;
38 typedef struct _GExpanderConverterClass  GExpanderConverterClass;
39
40 struct _GExpanderConverterClass
41 {
42   GObjectClass parent_class;
43 };
44
45 GType       g_expander_converter_get_type (void) G_GNUC_CONST;
46 GConverter *g_expander_converter_new      (void);
47
48
49
50 static void g_expander_converter_iface_init          (GConverterIface *iface);
51
52 struct _GExpanderConverter
53 {
54   GObject parent_instance;
55 };
56
57 G_DEFINE_TYPE_WITH_CODE (GExpanderConverter, g_expander_converter, G_TYPE_OBJECT,
58                          G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
59                                                 g_expander_converter_iface_init))
60
61 static void
62 g_expander_converter_class_init (GExpanderConverterClass *klass)
63 {
64 }
65
66 static void
67 g_expander_converter_init (GExpanderConverter *local)
68 {
69 }
70
71 GConverter *
72 g_expander_converter_new (void)
73 {
74   GConverter *conv;
75
76   conv = g_object_new (G_TYPE_EXPANDER_CONVERTER, NULL);
77
78   return conv;
79 }
80
81 static void
82 g_expander_converter_reset (GConverter *converter)
83 {
84 }
85
86 static GConverterResult
87 g_expander_converter_convert (GConverter *converter,
88                               const void *inbuf,
89                               gsize       inbuf_size,
90                               void       *outbuf,
91                               gsize       outbuf_size,
92                               GConverterFlags flags,
93                               gsize      *bytes_read,
94                               gsize      *bytes_written,
95                               GError    **error)
96 {
97   const guint8 *in, *in_end;
98   guint8 v, *out;
99   gsize i;
100   gsize block_size;
101
102   in = inbuf;
103   out = outbuf;
104   in_end = in + inbuf_size;
105
106   while (in < in_end)
107     {
108       v = *in;
109
110       if (v == 0)
111         block_size = 10;
112       else
113         block_size = v * 1000;
114
115       if (outbuf_size < block_size)
116         {
117           if (*bytes_read > 0)
118             return G_CONVERTER_CONVERTED;
119
120           g_set_error_literal (error, G_IO_ERROR,
121                                G_IO_ERROR_NO_SPACE,
122                                "No space in dest");
123           return G_CONVERTER_ERROR;
124         }
125
126       in++;
127       *bytes_read += 1;
128       *bytes_written += block_size;
129       outbuf_size -= block_size;
130       for (i = 0; i < block_size; i++)
131         *out++ = v;
132     }
133
134   if (in == in_end && (flags & G_CONVERTER_INPUT_AT_END))
135     return G_CONVERTER_FINISHED;
136   return G_CONVERTER_CONVERTED;
137 }
138
139 static void
140 g_expander_converter_iface_init (GConverterIface *iface)
141 {
142   iface->convert = g_expander_converter_convert;
143   iface->reset = g_expander_converter_reset;
144 }
145
146 #define G_TYPE_COMPRESSOR_CONVERTER         (g_compressor_converter_get_type ())
147 #define G_COMPRESSOR_CONVERTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverter))
148 #define G_COMPRESSOR_CONVERTER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverterClass))
149 #define G_IS_COMPRESSOR_CONVERTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_COMPRESSOR_CONVERTER))
150 #define G_IS_COMPRESSOR_CONVERTER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_COMPRESSOR_CONVERTER))
151 #define G_COMPRESSOR_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_COMPRESSOR_CONVERTER, GCompressorConverterClass))
152
153 typedef struct _GCompressorConverter       GCompressorConverter;
154 typedef struct _GCompressorConverterClass  GCompressorConverterClass;
155
156 struct _GCompressorConverterClass
157 {
158   GObjectClass parent_class;
159 };
160
161 GType       g_compressor_converter_get_type (void) G_GNUC_CONST;
162 GConverter *g_compressor_converter_new      (void);
163
164
165
166 static void g_compressor_converter_iface_init          (GConverterIface *iface);
167
168 struct _GCompressorConverter
169 {
170   GObject parent_instance;
171 };
172
173 G_DEFINE_TYPE_WITH_CODE (GCompressorConverter, g_compressor_converter, G_TYPE_OBJECT,
174                          G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
175                                                 g_compressor_converter_iface_init))
176
177 static void
178 g_compressor_converter_class_init (GCompressorConverterClass *klass)
179 {
180 }
181
182 static void
183 g_compressor_converter_init (GCompressorConverter *local)
184 {
185 }
186
187 GConverter *
188 g_compressor_converter_new (void)
189 {
190   GConverter *conv;
191
192   conv = g_object_new (G_TYPE_COMPRESSOR_CONVERTER, NULL);
193
194   return conv;
195 }
196
197 static void
198 g_compressor_converter_reset (GConverter *converter)
199 {
200 }
201
202 static GConverterResult
203 g_compressor_converter_convert (GConverter *converter,
204                                 const void *inbuf,
205                                 gsize       inbuf_size,
206                                 void       *outbuf,
207                                 gsize       outbuf_size,
208                                 GConverterFlags flags,
209                                 gsize      *bytes_read,
210                                 gsize      *bytes_written,
211                                 GError    **error)
212 {
213   const guint8 *in, *in_end;
214   guint8 v, *out;
215   gsize i;
216   gsize block_size;
217
218   in = inbuf;
219   out = outbuf;
220   in_end = in + inbuf_size;
221
222   while (in < in_end)
223     {
224       v = *in;
225
226       if (v == 0)
227         {
228           block_size = 0;
229           while (in+block_size < in_end && *(in+block_size) == 0)
230             block_size ++;
231         }
232       else
233         block_size = v * 1000;
234
235       /* Not enough data */
236       if ((gsize) (in_end - in) < block_size)
237         {
238           if (*bytes_read > 0)
239             break;
240           g_set_error_literal (error, G_IO_ERROR,
241                                G_IO_ERROR_PARTIAL_INPUT,
242                                "Need more data");
243           return G_CONVERTER_ERROR;
244         }
245
246       for (i = 0; i < block_size; i++)
247         {
248           if (*(in + i) != v)
249             {
250               if (*bytes_read > 0)
251                 break;
252               g_set_error_literal (error, G_IO_ERROR,
253                                    G_IO_ERROR_INVALID_DATA,
254                                    "invalid data");
255               return G_CONVERTER_ERROR;
256             }
257         }
258
259       if (v == 0 && (gsize) (in_end - in) == block_size && (flags & G_CONVERTER_INPUT_AT_END) == 0)
260         {
261           if (*bytes_read > 0)
262             break;
263           g_set_error_literal (error, G_IO_ERROR,
264                                G_IO_ERROR_PARTIAL_INPUT,
265                                "Need more data");
266           return G_CONVERTER_ERROR;
267         }
268
269       in += block_size;
270       *out++ = v;
271       *bytes_read += block_size;
272       *bytes_written += 1;
273     }
274
275   if (in == in_end && (flags & G_CONVERTER_INPUT_AT_END))
276     return G_CONVERTER_FINISHED;
277   return G_CONVERTER_CONVERTED;
278 }
279
280 static void
281 g_compressor_converter_iface_init (GConverterIface *iface)
282 {
283   iface->convert = g_compressor_converter_convert;
284   iface->reset = g_compressor_converter_reset;
285 }
286
287 guint8 unexpanded_data[] = { 0,1,3,4,5,6,7,3,12,0,0};
288
289 static void
290 test_expander (void)
291 {
292   guint8 *converted1, *converted2, *ptr;
293   gsize n_read, n_written;
294   gsize total_read;
295   gssize res;
296   GConverterResult cres;
297   GInputStream *mem, *cstream;
298   GOutputStream *mem_out, *cstream_out;
299   GConverter *expander;
300   GConverter *converter;
301   GError *error;
302   gsize i;
303
304   expander = g_expander_converter_new ();
305
306   converted1 = g_malloc (100*1000); /* Large enough */
307   converted2 = g_malloc (100*1000); /* Large enough */
308
309   cres = g_converter_convert (expander,
310                               unexpanded_data, sizeof(unexpanded_data),
311                               converted1, 100*1000,
312                               G_CONVERTER_INPUT_AT_END,
313                               &n_read, &n_written, NULL);
314
315   g_assert_cmpint (cres, ==, G_CONVERTER_FINISHED);
316   g_assert_cmpuint (n_read, ==, 11);
317   g_assert_cmpuint (n_written, ==, 41030);
318
319   g_converter_reset (expander);
320
321   mem = g_memory_input_stream_new_from_data (unexpanded_data,
322                                              sizeof (unexpanded_data),
323                                              NULL);
324   cstream = g_converter_input_stream_new (mem, expander);
325   g_assert_true (g_converter_input_stream_get_converter (G_CONVERTER_INPUT_STREAM (cstream)) == expander);
326   g_object_get (cstream, "converter", &converter, NULL);
327   g_assert_true (converter == expander);
328   g_object_unref (converter);
329   g_object_unref (mem);
330
331   total_read = 0;
332   ptr = converted2;
333   while (TRUE)
334     {
335       error = NULL;
336       res = g_input_stream_read (cstream,
337                                  ptr, 1,
338                                  NULL, &error);
339       g_assert_cmpint (res, !=, -1);
340       if (res == 0)
341         break;
342       ptr += res;
343       total_read += res;
344     }
345
346   g_assert_cmpmem (converted1, n_written, converted2, total_read);
347
348   g_converter_reset (expander);
349
350   mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
351   cstream_out = g_converter_output_stream_new (mem_out, expander);
352   g_assert_true (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (cstream_out)) == expander);
353   g_object_get (cstream_out, "converter", &converter, NULL);
354   g_assert_true (converter == expander);
355   g_object_unref (converter);
356   g_object_unref (mem_out);
357
358   for (i = 0; i < sizeof(unexpanded_data); i++)
359     {
360       error = NULL;
361       res = g_output_stream_write (cstream_out,
362                                    unexpanded_data + i, 1,
363                                    NULL, &error);
364       g_assert_cmpint (res, !=, -1);
365       if (res == 0)
366         {
367           g_assert_cmpuint (i, ==, sizeof(unexpanded_data) -1);
368           break;
369         }
370       g_assert_cmpint (res, ==, 1);
371     }
372
373   g_output_stream_close (cstream_out, NULL, NULL);
374
375   g_assert_cmpmem (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
376                    g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)),
377                    converted1, n_written);
378
379   g_free (converted1);
380   g_free (converted2);
381   g_object_unref (cstream);
382   g_object_unref (cstream_out);
383   g_object_unref (expander);
384 }
385
386 static void
387 test_compressor (void)
388 {
389   guint8 *converted, *expanded, *ptr;
390   gsize n_read, expanded_size;
391   gsize total_read;
392   gssize res;
393   GConverterResult cres;
394   GInputStream *mem, *cstream;
395   GOutputStream *mem_out, *cstream_out;
396   GConverter *expander, *compressor;
397   GError *error;
398   gsize i;
399
400   expander = g_expander_converter_new ();
401   expanded = g_malloc (100*1000); /* Large enough */
402   cres = g_converter_convert (expander,
403                               unexpanded_data, sizeof(unexpanded_data),
404                               expanded, 100*1000,
405                               G_CONVERTER_INPUT_AT_END,
406                               &n_read, &expanded_size, NULL);
407   g_assert_cmpint (cres, ==, G_CONVERTER_FINISHED);
408   g_assert_cmpuint (n_read, ==, 11);
409   g_assert_cmpuint (expanded_size, ==, 41030);
410
411   compressor = g_compressor_converter_new ();
412
413   converted = g_malloc (100*1000); /* Large enough */
414
415   mem = g_memory_input_stream_new_from_data (expanded,
416                                              expanded_size,
417                                              NULL);
418   cstream = g_converter_input_stream_new (mem, compressor);
419   g_object_unref (mem);
420
421   total_read = 0;
422   ptr = converted;
423   while (TRUE)
424     {
425       error = NULL;
426       res = g_input_stream_read (cstream,
427                                  ptr, 1,
428                                  NULL, &error);
429       g_assert_cmpint (res, !=, -1);
430       if (res == 0)
431         break;
432       ptr += res;
433       total_read += res;
434     }
435
436   /* "n_read - 1" because last 2 zeros are combined */
437   g_assert_cmpmem (unexpanded_data, n_read - 1, converted, total_read);
438
439   g_object_unref (cstream);
440
441   g_converter_reset (compressor);
442
443   mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
444   cstream_out = g_converter_output_stream_new (mem_out, compressor);
445   g_object_unref (mem_out);
446
447   for (i = 0; i < expanded_size; i++)
448     {
449       error = NULL;
450       res = g_output_stream_write (cstream_out,
451                                    expanded + i, 1,
452                                    NULL, &error);
453       g_assert_cmpint (res, !=, -1);
454       if (res == 0)
455         {
456           g_assert_cmpuint (i, ==, expanded_size -1);
457           break;
458         }
459       g_assert_cmpint (res, ==, 1);
460     }
461
462   g_output_stream_close (cstream_out, NULL, NULL);
463
464   /* "n_read - 1" because last 2 zeros are combined */
465   g_assert_cmpmem (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
466                    g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)),
467                    unexpanded_data,
468                    n_read - 1);
469
470   g_object_unref (cstream_out);
471
472   g_converter_reset (compressor);
473
474   memset (expanded, 5, 5*1000*2);
475
476   mem = g_memory_input_stream_new_from_data (expanded,
477                                              5*1000,
478                                              NULL);
479   cstream = g_converter_input_stream_new (mem, compressor);
480   g_object_unref (mem);
481
482   total_read = 0;
483   ptr = converted;
484   while (TRUE)
485     {
486       error = NULL;
487       res = g_input_stream_read (cstream,
488                                  ptr, 1,
489                                  NULL, &error);
490       g_assert_cmpint (res, !=, -1);
491       if (res == 0)
492         break;
493       ptr += res;
494       total_read += res;
495     }
496
497   g_assert_cmpuint (total_read, ==, 1);
498   g_assert_cmpuint (*converted, ==, 5);
499
500   g_object_unref (cstream);
501
502   mem = g_memory_input_stream_new_from_data (expanded,
503                                              5*1000 * 2,
504                                              NULL);
505   cstream = g_converter_input_stream_new (mem, compressor);
506   g_object_unref (mem);
507
508   total_read = 0;
509   ptr = converted;
510   while (TRUE)
511     {
512       error = NULL;
513       res = g_input_stream_read (cstream,
514                                  ptr, 1,
515                                  NULL, &error);
516       g_assert_cmpint (res, !=, -1);
517       if (res == 0)
518         break;
519       ptr += res;
520       total_read += res;
521     }
522
523   g_assert_cmpuint (total_read, ==, 2);
524   g_assert_cmpuint (converted[0], ==, 5);
525   g_assert_cmpuint (converted[1], ==, 5);
526
527   g_object_unref (cstream);
528
529   g_converter_reset (compressor);
530
531   mem = g_memory_input_stream_new_from_data (expanded,
532                                              5*1000 * 2 - 1,
533                                              NULL);
534   cstream = g_converter_input_stream_new (mem, compressor);
535   g_object_unref (mem);
536
537   total_read = 0;
538   ptr = converted;
539   while (TRUE)
540     {
541       error = NULL;
542       res = g_input_stream_read (cstream,
543                                  ptr, 1,
544                                  NULL, &error);
545       if (res == -1)
546         {
547           g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT);
548           g_error_free (error);
549           break;
550         }
551
552       g_assert_cmpint (res, !=, 0);
553       ptr += res;
554       total_read += res;
555     }
556
557   g_assert_cmpuint (total_read, ==, 1);
558   g_assert_cmpuint (converted[0], ==, 5);
559
560   g_object_unref (cstream);
561
562   g_free (expanded);
563   g_free (converted);
564   g_object_unref (expander);
565   g_object_unref (compressor);
566 }
567
568 #define LEFTOVER_SHORT_READ_SIZE 512
569
570 #define G_TYPE_LEFTOVER_CONVERTER         (g_leftover_converter_get_type ())
571 #define G_LEFTOVER_CONVERTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LEFTOVER_CONVERTER, GLeftoverConverter))
572 #define G_LEFTOVER_CONVERTER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LEFTOVER_CONVERTER, GLeftoverConverterClass))
573 #define G_IS_LEFTOVER_CONVERTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LEFTOVER_CONVERTER))
574 #define G_IS_LEFTOVER_CONVERTER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LEFTOVER_CONVERTER))
575 #define G_LEFTOVER_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LEFTOVER_CONVERTER, GLeftoverConverterClass))
576
577 typedef struct _GLeftoverConverter       GLeftoverConverter;
578 typedef struct _GLeftoverConverterClass  GLeftoverConverterClass;
579
580 struct _GLeftoverConverterClass
581 {
582   GObjectClass parent_class;
583 };
584
585 GType       g_leftover_converter_get_type (void) G_GNUC_CONST;
586 GConverter *g_leftover_converter_new      (void);
587
588
589
590 static void g_leftover_converter_iface_init          (GConverterIface *iface);
591
592 struct _GLeftoverConverter
593 {
594   GObject parent_instance;
595 };
596
597 G_DEFINE_TYPE_WITH_CODE (GLeftoverConverter, g_leftover_converter, G_TYPE_OBJECT,
598                          G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
599                                                 g_leftover_converter_iface_init))
600
601 static void
602 g_leftover_converter_class_init (GLeftoverConverterClass *klass)
603 {
604 }
605
606 static void
607 g_leftover_converter_init (GLeftoverConverter *local)
608 {
609 }
610
611 GConverter *
612 g_leftover_converter_new (void)
613 {
614   GConverter *conv;
615
616   conv = g_object_new (G_TYPE_LEFTOVER_CONVERTER, NULL);
617
618   return conv;
619 }
620
621 static void
622 g_leftover_converter_reset (GConverter *converter)
623 {
624 }
625
626 static GConverterResult
627 g_leftover_converter_convert (GConverter *converter,
628                               const void *inbuf,
629                               gsize       inbuf_size,
630                               void       *outbuf,
631                               gsize       outbuf_size,
632                               GConverterFlags flags,
633                               gsize      *bytes_read,
634                               gsize      *bytes_written,
635                               GError    **error)
636 {
637   if (outbuf_size == LEFTOVER_SHORT_READ_SIZE)
638     {
639       g_set_error_literal (error,
640                            G_IO_ERROR,
641                            G_IO_ERROR_PARTIAL_INPUT,
642                            "partial input");
643       return G_CONVERTER_ERROR;
644     }
645
646   if (inbuf_size < 100)
647     *bytes_read = *bytes_written = MIN (inbuf_size, outbuf_size);
648   else
649     *bytes_read = *bytes_written = MIN (inbuf_size - 10, outbuf_size);
650   memcpy (outbuf, inbuf, *bytes_written);
651
652   if (*bytes_read == inbuf_size && (flags & G_CONVERTER_INPUT_AT_END))
653     return G_CONVERTER_FINISHED;
654   return G_CONVERTER_CONVERTED;
655 }
656
657 static void
658 g_leftover_converter_iface_init (GConverterIface *iface)
659 {
660   iface->convert = g_leftover_converter_convert;
661   iface->reset = g_leftover_converter_reset;
662 }
663
664 #define LEFTOVER_BUFSIZE 8192
665 #define INTERNAL_BUFSIZE 4096
666
667 static void
668 test_converter_leftover (void)
669 {
670   gchar *orig, *converted;
671   gsize total_read;
672   gssize res;
673   goffset offset;
674   GInputStream *mem, *cstream;
675   GConverter *converter;
676   GError *error;
677   int i;
678
679   converter = g_leftover_converter_new ();
680
681   orig = g_malloc (LEFTOVER_BUFSIZE);
682   converted = g_malloc (LEFTOVER_BUFSIZE);
683   for (i = 0; i < LEFTOVER_BUFSIZE; i++)
684     orig[i] = i % 64 + 32;
685
686   mem = g_memory_input_stream_new_from_data (orig, LEFTOVER_BUFSIZE, NULL);
687   cstream = g_converter_input_stream_new (mem, G_CONVERTER (converter));
688   g_object_unref (mem);
689
690   total_read = 0;
691
692   error = NULL;
693   res = g_input_stream_read (cstream,
694                              converted, LEFTOVER_SHORT_READ_SIZE,
695                              NULL, &error);
696   g_assert_cmpint (res, ==, LEFTOVER_SHORT_READ_SIZE);
697   total_read += res;
698
699   offset = g_seekable_tell (G_SEEKABLE (mem));
700   g_assert_cmpint (offset, >, LEFTOVER_SHORT_READ_SIZE);
701   g_assert_cmpint (offset, <, LEFTOVER_BUFSIZE);
702
703   /* At this point, @cstream has both a non-empty input_buffer
704    * and a non-empty converted_buffer, which is the case
705    * we want to test.
706    */
707
708   while (TRUE)
709     {
710       error = NULL;
711       res = g_input_stream_read (cstream,
712                                  converted + total_read,
713                                  LEFTOVER_BUFSIZE - total_read,
714                                  NULL, &error);
715       g_assert_cmpint (res, >=, 0);
716       if (res == 0)
717         break;
718       total_read += res;
719   }
720
721   g_assert_cmpmem (orig, LEFTOVER_BUFSIZE, converted, total_read);
722
723   g_object_unref (cstream);
724   g_free (orig);
725   g_free (converted);
726   g_object_unref (converter);
727 }
728
729 #define DATA_LENGTH 1000000
730
731 typedef struct {
732   const gchar *path;
733   GZlibCompressorFormat format;
734   gint level;
735 } CompressorTest;
736
737 static void
738 test_roundtrip (gconstpointer data)
739 {
740   const CompressorTest *test = data;
741   GError *error = NULL;
742   guint32 *data0, *data1;
743   gsize data1_size;
744   gint i;
745   GInputStream *istream0, *istream1, *cistream1;
746   GOutputStream *ostream1, *ostream2, *costream1;
747   GConverter *compressor, *decompressor;
748   GZlibCompressorFormat fmt;
749   gint lvl;
750   GFileInfo *info;
751   GFileInfo *info2;
752
753   g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=619945");
754
755   data0 = g_malloc (DATA_LENGTH * sizeof (guint32));
756   for (i = 0; i < DATA_LENGTH; i++)
757     data0[i] = g_random_int ();
758
759   istream0 = g_memory_input_stream_new_from_data (data0,
760     DATA_LENGTH * sizeof (guint32), NULL);
761
762   ostream1 = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
763   compressor = G_CONVERTER (g_zlib_compressor_new (test->format, test->level));
764   info = g_file_info_new ();
765   g_file_info_set_name (info, "foo");
766   g_object_set (compressor, "file-info", info, NULL);
767   info2 = g_zlib_compressor_get_file_info (G_ZLIB_COMPRESSOR (compressor));
768   g_assert_true (info == info2);
769   g_object_unref (info);
770   costream1 = g_converter_output_stream_new (ostream1, compressor);
771   g_assert_true (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
772
773   g_output_stream_splice (costream1, istream0, 0, NULL, &error);
774   g_assert_no_error (error);
775
776   g_object_unref (costream1);
777
778   g_converter_reset (compressor);
779   g_object_get (compressor, "format", &fmt, "level", &lvl, NULL);
780   g_assert_cmpint (fmt, ==, test->format);
781   g_assert_cmpint (lvl, ==, test->level);
782   g_object_unref (compressor);
783   data1 = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream1));
784   data1_size = g_memory_output_stream_get_data_size (
785     G_MEMORY_OUTPUT_STREAM (ostream1));
786   g_object_unref (ostream1);
787   g_object_unref (istream0);
788
789   istream1 = g_memory_input_stream_new_from_data (data1, data1_size, g_free);
790   decompressor = G_CONVERTER (g_zlib_decompressor_new (test->format));
791   cistream1 = g_converter_input_stream_new (istream1, decompressor);
792
793   ostream2 = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
794
795   g_output_stream_splice (ostream2, cistream1, 0, NULL, &error);
796   g_assert_no_error (error);
797
798   g_assert_cmpmem (data0, DATA_LENGTH * sizeof (guint32),
799                    g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (ostream2)),
800                    g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (ostream2)));
801   g_object_unref (istream1);
802   g_converter_reset (decompressor);
803   g_object_get (decompressor, "format", &fmt, NULL);
804   g_assert_cmpint (fmt, ==, test->format);
805   g_object_unref (decompressor);
806   g_object_unref (cistream1);
807   g_object_unref (ostream2);
808   g_free (data0);
809 }
810
811 typedef struct {
812   const gchar *path;
813   const gchar *charset_in;
814   const gchar *text_in;
815   const gchar *charset_out;
816   const gchar *text_out;
817   gint n_fallbacks;
818 } CharsetTest;
819
820 static void
821 test_charset (gconstpointer data)
822 {
823   const CharsetTest *test = data;
824   GInputStream *in, *in2;
825   GConverter *conv;
826   gchar *buffer;
827   gsize count;
828   gsize bytes_read;
829   GError *error;
830   gboolean fallback;
831
832   conv = (GConverter *)g_charset_converter_new (test->charset_out, test->charset_in, NULL);
833   g_object_get (conv, "use-fallback", &fallback, NULL);
834   g_assert_false (fallback);
835
836   in = g_memory_input_stream_new_from_data (test->text_in, -1, NULL);
837   in2 = g_converter_input_stream_new (in, conv);
838
839   count = 2 * strlen (test->text_out);
840   buffer = g_malloc0 (count);
841   error = NULL;
842   g_input_stream_read_all (in2, buffer, count, &bytes_read, NULL, &error);
843   if (test->n_fallbacks == 0)
844     {
845       g_assert_no_error (error);
846       g_assert_cmpint (bytes_read, ==, strlen (test->text_out));
847       g_assert_cmpstr (buffer, ==, test->text_out);
848     }
849   else
850     {
851       g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA);
852       g_error_free (error);
853     }
854
855   g_free (buffer);
856   g_object_unref (in2);
857   g_object_unref (in);
858
859   if (test->n_fallbacks == 0)
860     {
861        g_object_unref (conv);
862        return;
863     }
864
865   g_converter_reset (conv);
866
867   g_assert_false (g_charset_converter_get_use_fallback (G_CHARSET_CONVERTER (conv)));
868   g_charset_converter_set_use_fallback (G_CHARSET_CONVERTER (conv), TRUE);
869
870   in = g_memory_input_stream_new_from_data (test->text_in, -1, NULL);
871   in2 = g_converter_input_stream_new (in, conv);
872
873   count = 2 * strlen (test->text_out);
874   buffer = g_malloc0 (count);
875   error = NULL;
876   g_input_stream_read_all (in2, buffer, count, &bytes_read, NULL, &error);
877   g_assert_no_error (error);
878   g_assert_cmpstr (buffer, ==, test->text_out);
879   g_assert_cmpint (bytes_read, ==, strlen (test->text_out));
880   g_assert_cmpint (test->n_fallbacks, ==, g_charset_converter_get_num_fallbacks (G_CHARSET_CONVERTER (conv)));
881
882   g_free (buffer);
883   g_object_unref (in2);
884   g_object_unref (in);
885
886   g_object_unref (conv);
887 }
888
889
890 static void
891 client_connected (GObject      *source,
892                   GAsyncResult *result,
893                   gpointer      user_data)
894 {
895   GSocketClient *client = G_SOCKET_CLIENT (source);
896   GSocketConnection **conn = user_data;
897   GError *error = NULL;
898
899   *conn = g_socket_client_connect_finish (client, result, &error);
900   g_assert_no_error (error);
901 }
902
903 static void
904 server_connected (GObject      *source,
905                   GAsyncResult *result,
906                   gpointer      user_data)
907 {
908   GSocketListener *listener = G_SOCKET_LISTENER (source);
909   GSocketConnection **conn = user_data;
910   GError *error = NULL;
911
912   *conn = g_socket_listener_accept_finish (listener, result, NULL, &error);
913   g_assert_no_error (error);
914 }
915
916 static void
917 make_socketpair (GIOStream **left,
918                  GIOStream **right)
919 {
920   GInetAddress *iaddr;
921   GSocketAddress *saddr, *effective_address;
922   GSocketListener *listener;
923   GSocketClient *client;
924   GError *error = NULL;
925   GSocketConnection *client_conn = NULL, *server_conn = NULL;
926
927   iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
928   saddr = g_inet_socket_address_new (iaddr, 0);
929   g_object_unref (iaddr);
930
931   listener = g_socket_listener_new ();
932   g_socket_listener_add_address (listener, saddr,
933                                  G_SOCKET_TYPE_STREAM,
934                                  G_SOCKET_PROTOCOL_TCP,
935                                  NULL,
936                                  &effective_address,
937                                  &error);
938   g_assert_no_error (error);
939   g_object_unref (saddr);
940
941   client = g_socket_client_new ();
942
943   g_socket_client_connect_async (client,
944                                  G_SOCKET_CONNECTABLE (effective_address),
945                                  NULL, client_connected, &client_conn);
946   g_socket_listener_accept_async (listener, NULL,
947                                   server_connected, &server_conn);
948
949   while (!client_conn || !server_conn)
950     g_main_context_iteration (NULL, TRUE);
951
952   g_object_unref (client);
953   g_object_unref (listener);
954   g_object_unref (effective_address);
955
956   *left = G_IO_STREAM (client_conn);
957   *right = G_IO_STREAM (server_conn);
958 }
959
960 static void
961 test_converter_pollable (void)
962 {
963   GIOStream *left, *right;
964   guint8 *converted, *inptr;
965   guint8 *expanded, *outptr, *expanded_end;
966   gsize n_read, expanded_size;
967   gsize total_read;
968   gssize res;
969   gboolean is_readable;
970   GConverterResult cres;
971   GInputStream *cstream;
972   GPollableInputStream *pollable_in;
973   GOutputStream *socket_out, *mem_out, *cstream_out;
974   GPollableOutputStream *pollable_out;
975   GConverter *expander, *compressor;
976   GError *error;
977   gsize i;
978
979   expander = g_expander_converter_new ();
980   expanded = g_malloc (100*1000); /* Large enough */
981   cres = g_converter_convert (expander,
982                               unexpanded_data, sizeof(unexpanded_data),
983                               expanded, 100*1000,
984                               G_CONVERTER_INPUT_AT_END,
985                               &n_read, &expanded_size, NULL);
986   g_assert_cmpint (cres, ==, G_CONVERTER_FINISHED);
987   g_assert_cmpuint (n_read, ==, 11);
988   g_assert_cmpuint (expanded_size, ==, 41030);
989   expanded_end = expanded + expanded_size;
990
991   make_socketpair (&left, &right);
992
993   compressor = g_compressor_converter_new ();
994
995   converted = g_malloc (100*1000); /* Large enough */
996
997   cstream = g_converter_input_stream_new (g_io_stream_get_input_stream (left),
998                                           compressor);
999   pollable_in = G_POLLABLE_INPUT_STREAM (cstream);
1000   g_assert_true (g_pollable_input_stream_can_poll (pollable_in));
1001
1002   socket_out = g_io_stream_get_output_stream (right);
1003
1004   total_read = 0;
1005   outptr = expanded;
1006   inptr = converted;
1007   while (TRUE)
1008     {
1009       error = NULL;
1010
1011       if (outptr < expanded_end)
1012         {
1013            res = g_output_stream_write (socket_out,
1014                                        outptr,
1015                                        MIN (1000, (expanded_end - outptr)),
1016                                        NULL, &error);
1017           g_assert_cmpint (res, >, 0);
1018           outptr += res;
1019         }
1020       else if (socket_out)
1021         {
1022           g_output_stream_close (socket_out, NULL, NULL);
1023           g_object_unref (right);
1024           socket_out = NULL;
1025         }
1026
1027       /* Wait a few ticks to check for the pipe to propagate the
1028        * write. We can’t wait on a GSource as that might affect the stream under
1029        * test, so just poll. */
1030       while (!g_pollable_input_stream_is_readable (pollable_in))
1031         g_usleep (80L);
1032
1033       is_readable = g_pollable_input_stream_is_readable (pollable_in);
1034       res = g_pollable_input_stream_read_nonblocking (pollable_in,
1035                                                       inptr, 1,
1036                                                       NULL, &error);
1037
1038       /* is_readable can be a false positive, but not a false negative */
1039       if (!is_readable)
1040         g_assert_cmpint (res, ==, -1);
1041
1042       /* Even after closing the write end, we can get WOULD_BLOCK (particularly
1043        * on FreeBSD), so we can’t make any assertions based on `!socket_out`.
1044        * This is because the FIN packets may still be in the out buffer of one
1045        * half of the socket pair, while the in buffer of the other half has some
1046        * data, but not enough for a full block for the converter to consume. */
1047
1048       if (res == -1)
1049         {
1050           g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK);
1051           g_error_free (error);
1052
1053           continue;
1054         }
1055
1056       if (res == 0)
1057         break;
1058       inptr += res;
1059       total_read += res;
1060     }
1061
1062   /* "n_read - 1" because last 2 zeros are combined */
1063   g_assert_cmpmem (unexpanded_data, n_read - 1, converted, total_read);
1064
1065   g_object_unref (cstream);
1066   g_object_unref (left);
1067
1068   g_converter_reset (compressor);
1069
1070   /* This doesn't actually test the behavior on
1071    * G_IO_ERROR_WOULD_BLOCK; to do that we'd need to implement a
1072    * custom GOutputStream that we could control blocking on.
1073    */
1074
1075   mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
1076   cstream_out = g_converter_output_stream_new (mem_out, compressor);
1077   g_object_unref (mem_out);
1078   pollable_out = G_POLLABLE_OUTPUT_STREAM (cstream_out);
1079   g_assert_true (g_pollable_output_stream_can_poll (pollable_out));
1080   g_assert_true (g_pollable_output_stream_is_writable (pollable_out));
1081
1082   for (i = 0; i < expanded_size; i++)
1083     {
1084       error = NULL;
1085       res = g_pollable_output_stream_write_nonblocking (pollable_out,
1086                                                         expanded + i, 1,
1087                                                         NULL, &error);
1088       g_assert_cmpint (res, !=, -1);
1089       if (res == 0)
1090         {
1091           g_assert_cmpuint (i, ==, expanded_size -1);
1092           break;
1093         }
1094       g_assert_cmpint (res, ==, 1);
1095     }
1096
1097   g_output_stream_close (cstream_out, NULL, NULL);
1098
1099   /* "n_read - 1" because last 2 zeros are combined */
1100   g_assert_cmpmem (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
1101                    g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)),
1102                    unexpanded_data,
1103                    n_read - 1);
1104
1105   g_object_unref (cstream_out);
1106
1107   g_free (expanded);
1108   g_free (converted);
1109   g_object_unref (expander);
1110   g_object_unref (compressor);
1111 }
1112
1113 static void
1114 test_truncation (gconstpointer data)
1115 {
1116   const CompressorTest *test = data;
1117   GError *error = NULL;
1118   guint32 *data0, *data1;
1119   gsize data1_size;
1120   gint i;
1121   GInputStream *istream0, *istream1, *cistream1;
1122   GOutputStream *ostream1, *ostream2, *costream1;
1123   GConverter *compressor, *decompressor;
1124
1125   data0 = g_malloc (DATA_LENGTH * sizeof (guint32));
1126   for (i = 0; i < DATA_LENGTH; i++)
1127     data0[i] = g_random_int ();
1128
1129   istream0 = g_memory_input_stream_new_from_data (data0,
1130     DATA_LENGTH * sizeof (guint32), NULL);
1131
1132   ostream1 = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
1133   compressor = G_CONVERTER (g_zlib_compressor_new (test->format, -1));
1134   costream1 = g_converter_output_stream_new (ostream1, compressor);
1135   g_assert_true (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
1136
1137   g_output_stream_splice (costream1, istream0, 0, NULL, &error);
1138   g_assert_no_error (error);
1139
1140   g_object_unref (costream1);
1141   g_object_unref (compressor);
1142
1143   data1 = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (ostream1));
1144   data1_size = g_memory_output_stream_get_data_size (
1145     G_MEMORY_OUTPUT_STREAM (ostream1));
1146   g_object_unref (ostream1);
1147   g_object_unref (istream0);
1148
1149   /* truncate */
1150   data1_size /= 2;
1151
1152   istream1 = g_memory_input_stream_new_from_data (data1, data1_size, g_free);
1153   decompressor = G_CONVERTER (g_zlib_decompressor_new (test->format));
1154   cistream1 = g_converter_input_stream_new (istream1, decompressor);
1155
1156   ostream2 = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
1157
1158   g_output_stream_splice (ostream2, cistream1, 0, NULL, &error);
1159   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT);
1160   g_error_free (error);
1161
1162   g_object_unref (istream1);
1163   g_object_unref (decompressor);
1164   g_object_unref (cistream1);
1165   g_object_unref (ostream2);
1166   g_free (data0);
1167 }
1168
1169 static void
1170 test_converter_basics (void)
1171 {
1172   GConverter *converter;
1173   GError *error = NULL;
1174   gchar *to;
1175   gchar *from;
1176
1177   converter = (GConverter *)g_charset_converter_new ("utf-8", "latin1", &error);
1178   g_assert_no_error (error);
1179   g_object_get (converter,
1180                 "to-charset", &to,
1181                 "from-charset", &from,
1182                 NULL);
1183
1184   g_assert_cmpstr (to, ==, "utf-8");
1185   g_assert_cmpstr (from, ==, "latin1");
1186
1187   g_free (to);
1188   g_free (from);
1189   g_object_unref (converter);
1190 }
1191
1192 int
1193 main (int   argc,
1194       char *argv[])
1195 {
1196   CompressorTest compressor_tests[] = {
1197     { "/converter-output-stream/roundtrip/zlib-0", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 0 },
1198     { "/converter-output-stream/roundtrip/zlib-9", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 9 },
1199     { "/converter-output-stream/roundtrip/gzip-0", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 0 },
1200     { "/converter-output-stream/roundtrip/gzip-9", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 9 },
1201     { "/converter-output-stream/roundtrip/raw-0", G_ZLIB_COMPRESSOR_FORMAT_RAW, 0 },
1202     { "/converter-output-stream/roundtrip/raw-9", G_ZLIB_COMPRESSOR_FORMAT_RAW, 9 },
1203   };
1204   CompressorTest truncation_tests[] = {
1205     { "/converter-input-stream/truncation/zlib", G_ZLIB_COMPRESSOR_FORMAT_ZLIB, 0 },
1206     { "/converter-input-stream/truncation/gzip", G_ZLIB_COMPRESSOR_FORMAT_GZIP, 0 },
1207     { "/converter-input-stream/truncation/raw", G_ZLIB_COMPRESSOR_FORMAT_RAW, 0 },
1208   };
1209   CharsetTest charset_tests[] = {
1210     { "/converter-input-stream/charset/utf8->latin1", "UTF-8", "\303\205rr Sant\303\251", "ISO-8859-1", "\305rr Sant\351", 0 },
1211     { "/converter-input-stream/charset/latin1->utf8", "ISO-8859-1", "\305rr Sant\351", "UTF-8", "\303\205rr Sant\303\251", 0 },
1212     { "/converter-input-stream/charset/fallbacks", "UTF-8", "Some characters just don't fit into latin1: πא", "ISO-8859-1", "Some characters just don't fit into latin1: \\CF\\80\\D7\\90", 4 },
1213   };
1214
1215   gsize i;
1216
1217   g_test_init (&argc, &argv, NULL);
1218
1219   g_test_add_func ("/converter/basics", test_converter_basics);
1220   g_test_add_func ("/converter-input-stream/expander", test_expander);
1221   g_test_add_func ("/converter-input-stream/compressor", test_compressor);
1222
1223   for (i = 0; i < G_N_ELEMENTS (compressor_tests); i++)
1224     g_test_add_data_func (compressor_tests[i].path, &compressor_tests[i], test_roundtrip);
1225
1226   for (i = 0; i < G_N_ELEMENTS (truncation_tests); i++)
1227     g_test_add_data_func (truncation_tests[i].path, &truncation_tests[i], test_truncation);
1228
1229   for (i = 0; i < G_N_ELEMENTS (charset_tests); i++)
1230     g_test_add_data_func (charset_tests[i].path, &charset_tests[i], test_charset);
1231
1232   g_test_add_func ("/converter-stream/pollable", test_converter_pollable);
1233   g_test_add_func ("/converter-stream/leftover", test_converter_leftover);
1234
1235   return g_test_run();
1236 }