Coverage Report

Created: 2025-06-13 06:55

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