Coverage Report

Created: 2025-07-11 06:31

/src/glib/gio/gconverterinputstream.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 *
3
 * Copyright (C) 2009 Red Hat, Inc.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Author: Alexander Larsson <alexl@redhat.com>
19
 */
20
21
#include "config.h"
22
23
#include <string.h>
24
25
#include "gconverterinputstream.h"
26
#include "gpollableinputstream.h"
27
#include "gcancellable.h"
28
#include "gioenumtypes.h"
29
#include "gioerror.h"
30
#include "glibintl.h"
31
32
33
/**
34
 * SECTION:gconverterinputstream
35
 * @short_description: Converter Input Stream
36
 * @include: gio/gio.h
37
 * @see_also: #GInputStream, #GConverter
38
 *
39
 * Converter input stream implements #GInputStream and allows
40
 * conversion of data of various types during reading.
41
 *
42
 * As of GLib 2.34, #GConverterInputStream implements
43
 * #GPollableInputStream.
44
 **/
45
46
168
#define INITIAL_BUFFER_SIZE 4096
47
48
typedef struct {
49
  char *data;
50
  gsize start;
51
  gsize end;
52
  gsize size;
53
} Buffer;
54
55
struct _GConverterInputStreamPrivate {
56
  gboolean at_input_end;
57
  gboolean finished;
58
  gboolean need_input;
59
  GConverter *converter;
60
  Buffer input_buffer;
61
  Buffer converted_buffer;
62
};
63
64
enum {
65
  PROP_0,
66
  PROP_CONVERTER
67
};
68
69
static void   g_converter_input_stream_set_property (GObject       *object,
70
                 guint          prop_id,
71
                 const GValue  *value,
72
                 GParamSpec    *pspec);
73
static void   g_converter_input_stream_get_property (GObject       *object,
74
                 guint          prop_id,
75
                 GValue        *value,
76
                 GParamSpec    *pspec);
77
static void   g_converter_input_stream_finalize     (GObject       *object);
78
static gssize g_converter_input_stream_read         (GInputStream  *stream,
79
                 void          *buffer,
80
                 gsize          count,
81
                 GCancellable  *cancellable,
82
                 GError       **error);
83
84
static gboolean g_converter_input_stream_can_poll         (GPollableInputStream *stream);
85
static gboolean g_converter_input_stream_is_readable      (GPollableInputStream *stream);
86
static gssize   g_converter_input_stream_read_nonblocking (GPollableInputStream  *stream,
87
                 void                  *buffer,
88
                 gsize                  size,
89
                 GError               **error);
90
91
static GSource *g_converter_input_stream_create_source    (GPollableInputStream *stream,
92
                 GCancellable          *cancellable);
93
94
static void g_converter_input_stream_pollable_iface_init  (GPollableInputStreamInterface *iface);
95
96
G_DEFINE_TYPE_WITH_CODE (GConverterInputStream,
97
       g_converter_input_stream,
98
       G_TYPE_FILTER_INPUT_STREAM,
99
                         G_ADD_PRIVATE (GConverterInputStream)
100
       G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
101
            g_converter_input_stream_pollable_iface_init))
102
103
static void
104
g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
105
1
{
106
1
  GObjectClass *object_class;
107
1
  GInputStreamClass *istream_class;
108
109
1
  object_class = G_OBJECT_CLASS (klass);
110
1
  object_class->get_property = g_converter_input_stream_get_property;
111
1
  object_class->set_property = g_converter_input_stream_set_property;
112
1
  object_class->finalize     = g_converter_input_stream_finalize;
113
114
1
  istream_class = G_INPUT_STREAM_CLASS (klass);
115
1
  istream_class->read_fn = g_converter_input_stream_read;
116
117
1
  g_object_class_install_property (object_class,
118
1
           PROP_CONVERTER,
119
1
           g_param_spec_object ("converter",
120
1
              P_("Converter"),
121
1
              P_("The converter object"),
122
1
              G_TYPE_CONVERTER,
123
1
              G_PARAM_READWRITE|
124
1
              G_PARAM_CONSTRUCT_ONLY|
125
1
              G_PARAM_STATIC_STRINGS));
126
127
1
}
128
129
static void
130
g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
131
1
{
132
1
  iface->can_poll = g_converter_input_stream_can_poll;
133
1
  iface->is_readable = g_converter_input_stream_is_readable;
134
1
  iface->read_nonblocking = g_converter_input_stream_read_nonblocking;
135
1
  iface->create_source = g_converter_input_stream_create_source;
136
1
}
137
138
static void
139
g_converter_input_stream_finalize (GObject *object)
140
116
{
141
116
  GConverterInputStreamPrivate *priv;
142
116
  GConverterInputStream        *stream;
143
144
116
  stream = G_CONVERTER_INPUT_STREAM (object);
145
116
  priv = stream->priv;
146
147
116
  g_free (priv->input_buffer.data);
148
116
  g_free (priv->converted_buffer.data);
149
116
  if (priv->converter)
150
116
    g_object_unref (priv->converter);
151
152
116
  G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object);
153
116
}
154
155
static void
156
g_converter_input_stream_set_property (GObject      *object,
157
               guint         prop_id,
158
               const GValue *value,
159
               GParamSpec   *pspec)
160
116
{
161
116
  GConverterInputStream *cstream;
162
163
116
  cstream = G_CONVERTER_INPUT_STREAM (object);
164
165
116
   switch (prop_id)
166
116
    {
167
116
    case PROP_CONVERTER:
168
116
      cstream->priv->converter = g_value_dup_object (value);
169
116
      break;
170
171
0
    default:
172
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173
0
      break;
174
116
    }
175
176
116
}
177
178
static void
179
g_converter_input_stream_get_property (GObject    *object,
180
               guint       prop_id,
181
               GValue     *value,
182
               GParamSpec *pspec)
183
0
{
184
0
  GConverterInputStreamPrivate *priv;
185
0
  GConverterInputStream        *cstream;
186
187
0
  cstream = G_CONVERTER_INPUT_STREAM (object);
188
0
  priv = cstream->priv;
189
190
0
  switch (prop_id)
191
0
    {
192
0
    case PROP_CONVERTER:
193
0
      g_value_set_object (value, priv->converter);
194
0
      break;
195
196
0
    default:
197
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198
0
      break;
199
0
    }
200
201
0
}
202
static void
203
g_converter_input_stream_init (GConverterInputStream *stream)
204
116
{
205
116
  stream->priv = g_converter_input_stream_get_instance_private (stream);
206
116
}
207
208
/**
209
 * g_converter_input_stream_new:
210
 * @base_stream: a #GInputStream
211
 * @converter: a #GConverter
212
 *
213
 * Creates a new converter input stream for the @base_stream.
214
 *
215
 * Returns: a new #GInputStream.
216
 **/
217
GInputStream *
218
g_converter_input_stream_new (GInputStream *base_stream,
219
                              GConverter   *converter)
220
116
{
221
116
  GInputStream *stream;
222
223
116
  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
224
225
116
  stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
226
116
                         "base-stream", base_stream,
227
116
       "converter", converter,
228
116
       NULL);
229
230
116
  return stream;
231
116
}
232
233
static gsize
234
buffer_data_size (Buffer *buffer)
235
8.47k
{
236
8.47k
  return buffer->end - buffer->start;
237
8.47k
}
238
239
static gsize
240
buffer_tailspace (Buffer *buffer)
241
560
{
242
560
  return buffer->size - buffer->end;
243
560
}
244
245
static char *
246
buffer_data (Buffer *buffer)
247
2.57k
{
248
2.57k
  return buffer->data + buffer->start;
249
2.57k
}
250
251
static void
252
buffer_consumed (Buffer *buffer,
253
     gsize count)
254
4.79k
{
255
4.79k
  buffer->start += count;
256
4.79k
  if (buffer->start == buffer->end)
257
2.55k
    buffer->start = buffer->end = 0;
258
4.79k
}
259
260
static void
261
buffer_read (Buffer *buffer,
262
       char *dest,
263
       gsize count)
264
2.44k
{
265
2.44k
  if (count != 0)
266
0
    memcpy (dest, buffer->data + buffer->start, count);
267
268
2.44k
  buffer_consumed (buffer, count);
269
2.44k
}
270
271
static void
272
compact_buffer (Buffer *buffer)
273
101
{
274
101
  gsize in_buffer;
275
276
101
  in_buffer = buffer_data_size (buffer);
277
101
  memmove (buffer->data,
278
101
     buffer->data + buffer->start,
279
101
     in_buffer);
280
101
  buffer->end -= buffer->start;
281
101
  buffer->start = 0;
282
101
}
283
284
static void
285
grow_buffer (Buffer *buffer)
286
672
{
287
672
  char *data;
288
672
  gsize size, in_buffer;
289
290
672
  if (buffer->size == 0)
291
168
    size = INITIAL_BUFFER_SIZE;
292
504
  else
293
504
    size = buffer->size * 2;
294
295
672
  data = g_malloc (size);
296
672
  in_buffer = buffer_data_size (buffer);
297
298
672
  if (in_buffer != 0)
299
0
    memcpy (data,
300
0
            buffer->data + buffer->start,
301
0
            in_buffer);
302
303
672
  g_free (buffer->data);
304
672
  buffer->data = data;
305
672
  buffer->end -= buffer->start;
306
672
  buffer->start = 0;
307
672
  buffer->size = size;
308
672
}
309
310
/* Ensures that the buffer can fit at_least_size bytes,
311
 * *including* the current in-buffer data */
312
static void
313
buffer_ensure_space (Buffer *buffer,
314
         gsize at_least_size)
315
269
{
316
269
  gsize in_buffer, left_to_fill;
317
318
269
  in_buffer = buffer_data_size (buffer);
319
320
269
  if (in_buffer >= at_least_size)
321
0
    return;
322
323
269
  left_to_fill = buffer_tailspace (buffer);
324
325
269
  if (in_buffer + left_to_fill >= at_least_size)
326
101
    {
327
      /* We fit in remaining space at end */
328
      /* If the copy is small, compact now anyway so we can fill more */
329
101
      if (in_buffer < 256)
330
101
  compact_buffer (buffer);
331
101
    }
332
168
  else if (buffer->size >= at_least_size)
333
0
    {
334
      /* We fit, but only if we compact */
335
0
      compact_buffer (buffer);
336
0
    }
337
168
  else
338
168
    {
339
      /* Need to grow buffer */
340
840
      while (buffer->size < at_least_size)
341
672
  grow_buffer (buffer);
342
168
    }
343
269
}
344
345
static gssize
346
fill_input_buffer (GConverterInputStream  *stream,
347
       gsize                   at_least_size,
348
       gboolean                blocking,
349
       GCancellable           *cancellable,
350
       GError                **error)
351
217
{
352
217
  GConverterInputStreamPrivate *priv;
353
217
  GInputStream *base_stream;
354
217
  gssize nread;
355
356
217
  priv = stream->priv;
357
358
217
  buffer_ensure_space (&priv->input_buffer, at_least_size);
359
360
217
  base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
361
217
  nread = g_pollable_stream_read (base_stream,
362
217
          priv->input_buffer.data + priv->input_buffer.end,
363
217
          buffer_tailspace (&priv->input_buffer),
364
217
          blocking,
365
217
          cancellable,
366
217
          error);
367
368
217
  if (nread > 0)
369
153
    {
370
153
      priv->input_buffer.end += nread;
371
153
      priv->need_input = FALSE;
372
153
    }
373
374
217
  return nread;
375
217
}
376
377
378
static gssize
379
read_internal (GInputStream *stream,
380
         void         *buffer,
381
         gsize         count,
382
         gboolean      blocking,
383
         GCancellable *cancellable,
384
         GError      **error)
385
2.44k
{
386
2.44k
  GConverterInputStream *cstream;
387
2.44k
  GConverterInputStreamPrivate *priv;
388
2.44k
  gsize available, total_bytes_read;
389
2.44k
  gssize nread;
390
2.44k
  GConverterResult res;
391
2.44k
  gsize bytes_read;
392
2.44k
  gsize bytes_written;
393
2.44k
  GError *my_error;
394
2.44k
  GError *my_error2;
395
396
2.44k
  cstream = G_CONVERTER_INPUT_STREAM (stream);
397
2.44k
  priv = cstream->priv;
398
399
2.44k
  available = buffer_data_size (&priv->converted_buffer);
400
401
2.44k
  if (available > 0 &&
402
2.44k
      count <= available)
403
0
    {
404
      /* Converted data available, return that */
405
0
      buffer_read (&priv->converted_buffer, buffer, count);
406
0
      return count;
407
0
    }
408
409
  /* Full request not available, read all currently available and request
410
     refill/conversion for more */
411
412
2.44k
  buffer_read (&priv->converted_buffer, buffer, available);
413
414
2.44k
  total_bytes_read = available;
415
2.44k
  buffer = (char *) buffer + available;
416
2.44k
  count -= available;
417
418
  /* If there is no data to convert, and no pre-converted data,
419
     do some i/o for more input */
420
2.44k
  if (buffer_data_size (&priv->input_buffer) == 0 &&
421
2.44k
      total_bytes_read == 0 &&
422
2.44k
      !priv->at_input_end)
423
195
    {
424
195
      nread = fill_input_buffer (cstream, count, blocking, cancellable, error);
425
195
      if (nread < 0)
426
0
  return -1;
427
195
      if (nread == 0)
428
42
  priv->at_input_end = TRUE;
429
195
    }
430
431
  /* First try to convert any available data (or state) directly to the user buffer: */
432
2.44k
  if (!priv->finished)
433
2.42k
    {
434
2.42k
      my_error = NULL;
435
2.42k
      res = g_converter_convert (priv->converter,
436
2.42k
         buffer_data (&priv->input_buffer),
437
2.42k
         buffer_data_size (&priv->input_buffer),
438
2.42k
         buffer, count,
439
2.42k
         priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
440
2.42k
         &bytes_read,
441
2.42k
         &bytes_written,
442
2.42k
         &my_error);
443
2.42k
      if (res != G_CONVERTER_ERROR)
444
2.35k
  {
445
2.35k
    total_bytes_read += bytes_written;
446
2.35k
    buffer_consumed (&priv->input_buffer, bytes_read);
447
2.35k
    if (res == G_CONVERTER_FINISHED)
448
21
      priv->finished = TRUE; /* We're done converting */
449
2.35k
  }
450
73
      else if (total_bytes_read == 0 &&
451
73
         !g_error_matches (my_error,
452
73
         G_IO_ERROR,
453
73
         G_IO_ERROR_PARTIAL_INPUT) &&
454
73
         !g_error_matches (my_error,
455
43
         G_IO_ERROR,
456
43
         G_IO_ERROR_NO_SPACE))
457
43
  {
458
    /* No previously read data and no "special" error, return error */
459
43
    g_propagate_error (error, my_error);
460
43
    return -1;
461
43
  }
462
30
      else
463
30
  g_error_free (my_error);
464
2.42k
    }
465
466
  /* We had some pre-converted data and/or we converted directly to the
467
     user buffer */
468
2.40k
  if (total_bytes_read > 0)
469
2.32k
    return total_bytes_read;
470
471
  /* If there is no more to convert, return EOF */
472
73
  if (priv->finished)
473
21
    {
474
21
      g_assert (buffer_data_size (&priv->converted_buffer) == 0);
475
21
      return 0;
476
21
    }
477
478
  /* There was "complexity" in the straight-to-buffer conversion,
479
   * convert to our own buffer and write from that.
480
   * At this point we didn't produce any data into @buffer.
481
   */
482
483
  /* Ensure we have *some* initial target space */
484
52
  buffer_ensure_space (&priv->converted_buffer, count);
485
486
74
  while (TRUE)
487
74
    {
488
74
      g_assert (!priv->finished);
489
490
      /* Try to convert to our buffer */
491
74
      my_error = NULL;
492
74
      res = g_converter_convert (priv->converter,
493
74
         buffer_data (&priv->input_buffer),
494
74
         buffer_data_size (&priv->input_buffer),
495
74
         buffer_data (&priv->converted_buffer),
496
74
         buffer_tailspace (&priv->converted_buffer),
497
74
         priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
498
74
         &bytes_read,
499
74
         &bytes_written,
500
74
         &my_error);
501
74
      if (res != G_CONVERTER_ERROR)
502
0
  {
503
0
    priv->converted_buffer.end += bytes_written;
504
0
    buffer_consumed (&priv->input_buffer, bytes_read);
505
506
    /* Maybe we consumed without producing any output */
507
0
    if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED)
508
0
      continue; /* Convert more */
509
510
0
    if (res == G_CONVERTER_FINISHED)
511
0
      priv->finished = TRUE;
512
513
0
    total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer));
514
0
    buffer_read (&priv->converted_buffer, buffer, total_bytes_read);
515
516
0
    g_assert (priv->finished || total_bytes_read > 0);
517
518
0
    return total_bytes_read;
519
0
  }
520
521
      /* There was some kind of error filling our buffer */
522
523
74
      if (g_error_matches (my_error,
524
74
         G_IO_ERROR,
525
74
         G_IO_ERROR_PARTIAL_INPUT) &&
526
74
    !priv->at_input_end)
527
22
  {
528
    /* Need more data */
529
22
    my_error2 = NULL;
530
22
    nread = fill_input_buffer (cstream,
531
22
             buffer_data_size (&priv->input_buffer) + 4096,
532
22
             blocking,
533
22
             cancellable,
534
22
             &my_error2);
535
22
    if (nread < 0)
536
0
      {
537
        /* Can't read any more data, return that error */
538
0
        g_error_free (my_error);
539
0
        g_propagate_error (error, my_error2);
540
0
        priv->need_input = TRUE;
541
0
        return -1;
542
0
      }
543
22
    else if (nread == 0)
544
22
      {
545
        /* End of file, try INPUT_AT_END */
546
22
        priv->at_input_end = TRUE;
547
22
      }
548
22
    g_error_free (my_error);
549
22
    continue;
550
22
  }
551
552
52
      if (g_error_matches (my_error,
553
52
         G_IO_ERROR,
554
52
         G_IO_ERROR_NO_SPACE))
555
0
  {
556
    /* Need more destination space, grow it
557
     * Note: if we actually grow the buffer (as opposed to compacting it),
558
     * this will double the size, not just add one byte. */
559
0
    buffer_ensure_space (&priv->converted_buffer,
560
0
             priv->converted_buffer.size + 1);
561
0
    g_error_free (my_error);
562
0
    continue;
563
0
  }
564
565
      /* Any other random error, return it */
566
52
      g_propagate_error (error, my_error);
567
52
      return -1;
568
52
    }
569
570
52
  g_assert_not_reached ();
571
0
}
572
573
static gssize
574
g_converter_input_stream_read (GInputStream *stream,
575
             void         *buffer,
576
             gsize         count,
577
             GCancellable *cancellable,
578
             GError      **error)
579
2.44k
{
580
2.44k
  return read_internal (stream, buffer, count, TRUE, cancellable, error);
581
2.44k
}
582
583
static gboolean
584
g_converter_input_stream_can_poll (GPollableInputStream *stream)
585
0
{
586
0
  GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
587
588
0
  return (G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
589
0
    g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)));
590
0
}
591
592
static gboolean
593
g_converter_input_stream_is_readable (GPollableInputStream *stream)
594
0
{
595
0
  GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
596
0
  GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream);
597
598
0
  if (buffer_data_size (&cstream->priv->converted_buffer))
599
0
    return TRUE;
600
0
  else if (buffer_data_size (&cstream->priv->input_buffer) &&
601
0
     !cstream->priv->need_input)
602
0
    return TRUE;
603
0
  else
604
0
    return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream));
605
0
}
606
607
static gssize
608
g_converter_input_stream_read_nonblocking (GPollableInputStream  *stream,
609
             void                  *buffer,
610
             gsize                  count,
611
             GError               **error)
612
0
{
613
0
  return read_internal (G_INPUT_STREAM (stream), buffer, count,
614
0
      FALSE, NULL, error);
615
0
}
616
617
static GSource *
618
g_converter_input_stream_create_source (GPollableInputStream *stream,
619
          GCancellable         *cancellable)
620
0
{
621
0
  GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
622
0
  GSource *base_source, *pollable_source;
623
624
0
  if (g_pollable_input_stream_is_readable (stream))
625
0
    base_source = g_timeout_source_new (0);
626
0
  else
627
0
    base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL);
628
629
0
  pollable_source = g_pollable_source_new_full (stream, base_source,
630
0
            cancellable);
631
0
  g_source_unref (base_source);
632
633
0
  return pollable_source;
634
0
}
635
636
637
/**
638
 * g_converter_input_stream_get_converter:
639
 * @converter_stream: a #GConverterInputStream
640
 *
641
 * Gets the #GConverter that is used by @converter_stream.
642
 *
643
 * Returns: (transfer none): the converter of the converter input stream
644
 *
645
 * Since: 2.24
646
 */
647
GConverter *
648
g_converter_input_stream_get_converter (GConverterInputStream *converter_stream)
649
0
{
650
0
  return converter_stream->priv->converter;
651
0
}