Coverage Report

Created: 2026-06-10 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glib/gio/gconverterinputstream.c
Line
Count
Source
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
20.2k
#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
85.7k
G_DEFINE_TYPE_WITH_CODE (GConverterInputStream,
97
85.7k
       g_converter_input_stream,
98
85.7k
       G_TYPE_FILTER_INPUT_STREAM,
99
85.7k
                         G_ADD_PRIVATE (GConverterInputStream)
100
85.7k
       G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
101
85.7k
            g_converter_input_stream_pollable_iface_init))
102
85.7k
103
85.7k
static void
104
85.7k
g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
105
85.7k
{
106
4
  GObjectClass *object_class;
107
4
  GInputStreamClass *istream_class;
108
109
4
  object_class = G_OBJECT_CLASS (klass);
110
4
  object_class->get_property = g_converter_input_stream_get_property;
111
4
  object_class->set_property = g_converter_input_stream_set_property;
112
4
  object_class->finalize     = g_converter_input_stream_finalize;
113
114
4
  istream_class = G_INPUT_STREAM_CLASS (klass);
115
4
  istream_class->read_fn = g_converter_input_stream_read;
116
117
4
  g_object_class_install_property (object_class,
118
4
           PROP_CONVERTER,
119
4
           g_param_spec_object ("converter",
120
4
              P_("Converter"),
121
4
              P_("The converter object"),
122
4
              G_TYPE_CONVERTER,
123
4
              G_PARAM_READWRITE|
124
4
              G_PARAM_CONSTRUCT_ONLY|
125
4
              G_PARAM_STATIC_STRINGS));
126
127
4
}
128
129
static void
130
g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
131
4
{
132
4
  iface->can_poll = g_converter_input_stream_can_poll;
133
4
  iface->is_readable = g_converter_input_stream_is_readable;
134
4
  iface->read_nonblocking = g_converter_input_stream_read_nonblocking;
135
4
  iface->create_source = g_converter_input_stream_create_source;
136
4
}
137
138
static void
139
g_converter_input_stream_finalize (GObject *object)
140
16.7k
{
141
16.7k
  GConverterInputStreamPrivate *priv;
142
16.7k
  GConverterInputStream        *stream;
143
144
16.7k
  stream = G_CONVERTER_INPUT_STREAM (object);
145
16.7k
  priv = stream->priv;
146
147
16.7k
  g_free (priv->input_buffer.data);
148
16.7k
  g_free (priv->converted_buffer.data);
149
16.7k
  if (priv->converter)
150
16.7k
    g_object_unref (priv->converter);
151
152
16.7k
  G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object);
153
16.7k
}
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
16.7k
{
161
16.7k
  GConverterInputStream *cstream;
162
163
16.7k
  cstream = G_CONVERTER_INPUT_STREAM (object);
164
165
16.7k
   switch (prop_id)
166
16.7k
    {
167
16.7k
    case PROP_CONVERTER:
168
16.7k
      cstream->priv->converter = g_value_dup_object (value);
169
16.7k
      break;
170
171
0
    default:
172
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173
0
      break;
174
16.7k
    }
175
176
16.7k
}
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
16.7k
{
205
16.7k
  stream->priv = g_converter_input_stream_get_instance_private (stream);
206
16.7k
}
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
16.7k
{
221
16.7k
  GInputStream *stream;
222
223
16.7k
  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
224
225
16.7k
  stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
226
16.7k
                         "base-stream", base_stream,
227
16.7k
       "converter", converter,
228
16.7k
       NULL);
229
230
16.7k
  return stream;
231
16.7k
}
232
233
static gsize
234
buffer_data_size (Buffer *buffer)
235
253k
{
236
253k
  return buffer->end - buffer->start;
237
253k
}
238
239
static gsize
240
buffer_tailspace (Buffer *buffer)
241
102k
{
242
102k
  return buffer->size - buffer->end;
243
102k
}
244
245
static char *
246
buffer_data (Buffer *buffer)
247
99.4k
{
248
99.4k
  return buffer->data + buffer->start;
249
99.4k
}
250
251
static void
252
buffer_consumed (Buffer *buffer,
253
     gsize count)
254
80.8k
{
255
80.8k
  buffer->start += count;
256
80.8k
  if (buffer->start == buffer->end)
257
57.2k
    buffer->start = buffer->end = 0;
258
80.8k
}
259
260
static void
261
buffer_read (Buffer *buffer,
262
       char *dest,
263
       gsize count)
264
36.6k
{
265
36.6k
  if (count != 0)
266
1.21k
    memcpy (dest, buffer->data + buffer->start, count);
267
268
36.6k
  buffer_consumed (buffer, count);
269
36.6k
}
270
271
static void
272
compact_buffer (Buffer *buffer)
273
16.5k
{
274
16.5k
  gsize in_buffer;
275
276
16.5k
  in_buffer = buffer_data_size (buffer);
277
16.5k
  memmove (buffer->data,
278
16.5k
     buffer->data + buffer->start,
279
16.5k
     in_buffer);
280
16.5k
  buffer->end -= buffer->start;
281
16.5k
  buffer->start = 0;
282
16.5k
}
283
284
static void
285
grow_buffer (Buffer *buffer)
286
28.9k
{
287
28.9k
  char *data;
288
28.9k
  gsize size, in_buffer;
289
290
28.9k
  if (buffer->size == 0)
291
20.2k
    size = INITIAL_BUFFER_SIZE;
292
8.62k
  else
293
8.62k
    size = buffer->size * 2;
294
295
28.9k
  data = g_malloc (size);
296
28.9k
  in_buffer = buffer_data_size (buffer);
297
298
28.9k
  if (in_buffer != 0)
299
0
    memcpy (data,
300
0
            buffer->data + buffer->start,
301
0
            in_buffer);
302
303
28.9k
  g_free (buffer->data);
304
28.9k
  buffer->data = data;
305
28.9k
  buffer->end -= buffer->start;
306
28.9k
  buffer->start = 0;
307
28.9k
  buffer->size = size;
308
28.9k
}
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
36.8k
{
316
36.8k
  gsize in_buffer, left_to_fill;
317
318
36.8k
  in_buffer = buffer_data_size (buffer);
319
320
36.8k
  if (in_buffer >= at_least_size)
321
0
    return;
322
323
36.8k
  left_to_fill = buffer_tailspace (buffer);
324
325
36.8k
  if (in_buffer + left_to_fill >= at_least_size)
326
16.5k
    {
327
      /* We fit in remaining space at end */
328
      /* If the copy is small, compact now anyway so we can fill more */
329
16.5k
      if (in_buffer < 256)
330
16.5k
  compact_buffer (buffer);
331
16.5k
    }
332
20.2k
  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
20.2k
  else
338
20.2k
    {
339
      /* Need to grow buffer */
340
49.1k
      while (buffer->size < at_least_size)
341
28.9k
  grow_buffer (buffer);
342
20.2k
    }
343
36.8k
}
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
33.2k
{
352
33.2k
  GConverterInputStreamPrivate *priv;
353
33.2k
  GInputStream *base_stream;
354
33.2k
  gssize nread;
355
356
33.2k
  priv = stream->priv;
357
358
33.2k
  buffer_ensure_space (&priv->input_buffer, at_least_size);
359
360
33.2k
  base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
361
33.2k
  nread = g_pollable_stream_read (base_stream,
362
33.2k
          priv->input_buffer.data + priv->input_buffer.end,
363
33.2k
          buffer_tailspace (&priv->input_buffer),
364
33.2k
          blocking,
365
33.2k
          cancellable,
366
33.2k
          error);
367
368
33.2k
  if (nread > 0)
369
29.7k
    {
370
29.7k
      priv->input_buffer.end += nread;
371
29.7k
      priv->need_input = FALSE;
372
29.7k
    }
373
374
33.2k
  return nread;
375
33.2k
}
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
35.4k
{
386
35.4k
  GConverterInputStream *cstream;
387
35.4k
  GConverterInputStreamPrivate *priv;
388
35.4k
  gsize available, total_bytes_read;
389
35.4k
  gssize nread;
390
35.4k
  GConverterResult res;
391
35.4k
  gsize bytes_read;
392
35.4k
  gsize bytes_written;
393
35.4k
  GError *my_error;
394
35.4k
  GError *my_error2;
395
396
35.4k
  cstream = G_CONVERTER_INPUT_STREAM (stream);
397
35.4k
  priv = cstream->priv;
398
399
35.4k
  available = buffer_data_size (&priv->converted_buffer);
400
401
35.4k
  if (available > 0 &&
402
0
      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
35.4k
  buffer_read (&priv->converted_buffer, buffer, available);
413
414
35.4k
  total_bytes_read = available;
415
35.4k
  buffer = (char *) buffer + available;
416
35.4k
  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
35.4k
  if (buffer_data_size (&priv->input_buffer) == 0 &&
421
18.1k
      total_bytes_read == 0 &&
422
18.1k
      !priv->at_input_end)
423
17.0k
    {
424
17.0k
      nread = fill_input_buffer (cstream, count, blocking, cancellable, error);
425
17.0k
      if (nread < 0)
426
0
  return -1;
427
17.0k
      if (nread == 0)
428
130
  priv->at_input_end = TRUE;
429
17.0k
    }
430
431
  /* First try to convert any available data (or state) directly to the user buffer: */
432
35.4k
  if (!priv->finished)
433
34.2k
    {
434
34.2k
      my_error = NULL;
435
34.2k
      res = g_converter_convert (priv->converter,
436
34.2k
         buffer_data (&priv->input_buffer),
437
34.2k
         buffer_data_size (&priv->input_buffer),
438
34.2k
         buffer, count,
439
34.2k
         priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
440
34.2k
         &bytes_read,
441
34.2k
         &bytes_written,
442
34.2k
         &my_error);
443
34.2k
      if (res != G_CONVERTER_ERROR)
444
30.1k
  {
445
30.1k
    total_bytes_read += bytes_written;
446
30.1k
    buffer_consumed (&priv->input_buffer, bytes_read);
447
30.1k
    if (res == G_CONVERTER_FINISHED)
448
1.34k
      priv->finished = TRUE; /* We're done converting */
449
30.1k
  }
450
4.09k
      else if (total_bytes_read == 0 &&
451
4.09k
         !g_error_matches (my_error,
452
4.09k
         G_IO_ERROR,
453
4.09k
         G_IO_ERROR_PARTIAL_INPUT) &&
454
3.97k
         !g_error_matches (my_error,
455
3.97k
         G_IO_ERROR,
456
3.97k
         G_IO_ERROR_NO_SPACE))
457
3.97k
  {
458
    /* No previously read data and no "special" error, return error */
459
3.97k
    g_propagate_error (error, my_error);
460
3.97k
    return -1;
461
3.97k
  }
462
113
      else
463
113
  g_error_free (my_error);
464
34.2k
    }
465
466
  /* We had some pre-converted data and/or we converted directly to the
467
     user buffer */
468
31.4k
  if (total_bytes_read > 0)
469
26.6k
    return total_bytes_read;
470
471
  /* If there is no more to convert, return EOF */
472
4.80k
  if (priv->finished)
473
1.20k
    {
474
1.20k
      g_assert (buffer_data_size (&priv->converted_buffer) == 0);
475
1.20k
      return 0;
476
1.20k
    }
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
3.59k
  buffer_ensure_space (&priv->converted_buffer, count);
485
486
32.5k
  while (TRUE)
487
32.5k
    {
488
32.5k
      g_assert (!priv->finished);
489
490
      /* Try to convert to our buffer */
491
32.5k
      my_error = NULL;
492
32.5k
      res = g_converter_convert (priv->converter,
493
32.5k
         buffer_data (&priv->input_buffer),
494
32.5k
         buffer_data_size (&priv->input_buffer),
495
32.5k
         buffer_data (&priv->converted_buffer),
496
32.5k
         buffer_tailspace (&priv->converted_buffer),
497
32.5k
         priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
498
32.5k
         &bytes_read,
499
32.5k
         &bytes_written,
500
32.5k
         &my_error);
501
32.5k
      if (res != G_CONVERTER_ERROR)
502
13.9k
  {
503
13.9k
    priv->converted_buffer.end += bytes_written;
504
13.9k
    buffer_consumed (&priv->input_buffer, bytes_read);
505
506
    /* Maybe we consumed without producing any output */
507
13.9k
    if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED)
508
12.7k
      continue; /* Convert more */
509
510
1.21k
    if (res == G_CONVERTER_FINISHED)
511
1.13k
      priv->finished = TRUE;
512
513
1.21k
    total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer));
514
1.21k
    buffer_read (&priv->converted_buffer, buffer, total_bytes_read);
515
516
1.21k
    g_assert (priv->finished || total_bytes_read > 0);
517
518
1.21k
    return total_bytes_read;
519
1.21k
  }
520
521
      /* There was some kind of error filling our buffer */
522
523
18.6k
      if (g_error_matches (my_error,
524
18.6k
         G_IO_ERROR,
525
18.6k
         G_IO_ERROR_PARTIAL_INPUT) &&
526
18.6k
    !priv->at_input_end)
527
16.2k
  {
528
    /* Need more data */
529
16.2k
    my_error2 = NULL;
530
16.2k
    nread = fill_input_buffer (cstream,
531
16.2k
             buffer_data_size (&priv->input_buffer) + 4096,
532
16.2k
             blocking,
533
16.2k
             cancellable,
534
16.2k
             &my_error2);
535
16.2k
    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
16.2k
    else if (nread == 0)
544
3.40k
      {
545
        /* End of file, try INPUT_AT_END */
546
3.40k
        priv->at_input_end = TRUE;
547
3.40k
      }
548
16.2k
    g_error_free (my_error);
549
16.2k
    continue;
550
16.2k
  }
551
552
2.38k
      if (g_error_matches (my_error,
553
2.38k
         G_IO_ERROR,
554
2.38k
         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
2.38k
      g_propagate_error (error, my_error);
567
2.38k
      return -1;
568
2.38k
    }
569
570
3.59k
  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
35.4k
{
580
35.4k
  return read_internal (stream, buffer, count, TRUE, cancellable, error);
581
35.4k
}
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
}