Coverage Report

Created: 2026-01-09 07:21

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