Coverage Report

Created: 2025-07-01 07:09

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