Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/gmemoryinputstream.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 * 
3
 * Copyright (C) 2006-2007 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: Christian Kellner <gicmo@gnome.org> 
21
 */
22
23
#include "config.h"
24
#include "gmemoryinputstream.h"
25
#include "gpollableinputstream.h"
26
#include "ginputstream.h"
27
#include "gseekable.h"
28
#include "string.h"
29
#include "gtask.h"
30
#include "gioerror.h"
31
#include "glibintl.h"
32
33
34
/**
35
 * GMemoryInputStream:
36
 *
37
 * `GMemoryInputStream` is a class for using arbitrary
38
 * memory chunks as input for GIO streaming input operations.
39
 *
40
 * As of GLib 2.34, `GMemoryInputStream` implements
41
 * [iface@Gio.PollableInputStream].
42
 */
43
44
struct _GMemoryInputStreamPrivate {
45
  GSList *chunks;
46
  gsize   len;
47
  gsize   pos;
48
};
49
50
static gssize   g_memory_input_stream_read         (GInputStream         *stream,
51
                void                 *buffer,
52
                gsize                 count,
53
                GCancellable         *cancellable,
54
                GError              **error);
55
static gssize   g_memory_input_stream_skip         (GInputStream         *stream,
56
                gsize                 count,
57
                GCancellable         *cancellable,
58
                GError              **error);
59
static gboolean g_memory_input_stream_close        (GInputStream         *stream,
60
                GCancellable         *cancellable,
61
                GError              **error);
62
static void     g_memory_input_stream_skip_async   (GInputStream         *stream,
63
                gsize                 count,
64
                int                   io_priority,
65
                GCancellable         *cancellabl,
66
                GAsyncReadyCallback   callback,
67
                gpointer              datae);
68
static gssize   g_memory_input_stream_skip_finish  (GInputStream         *stream,
69
                GAsyncResult         *result,
70
                GError              **error);
71
static void     g_memory_input_stream_close_async  (GInputStream         *stream,
72
                int                   io_priority,
73
                GCancellable         *cancellabl,
74
                GAsyncReadyCallback   callback,
75
                gpointer              data);
76
static gboolean g_memory_input_stream_close_finish (GInputStream         *stream,
77
                GAsyncResult         *result,
78
                GError              **error);
79
80
static void     g_memory_input_stream_seekable_iface_init (GSeekableIface  *iface);
81
static goffset  g_memory_input_stream_tell                (GSeekable       *seekable);
82
static gboolean g_memory_input_stream_can_seek            (GSeekable       *seekable);
83
static gboolean g_memory_input_stream_seek                (GSeekable       *seekable,
84
                                                           goffset          offset,
85
                                                           GSeekType        type,
86
                                                           GCancellable    *cancellable,
87
                                                           GError         **error);
88
static gboolean g_memory_input_stream_can_truncate        (GSeekable       *seekable);
89
static gboolean g_memory_input_stream_truncate            (GSeekable       *seekable,
90
                                                           goffset          offset,
91
                                                           GCancellable    *cancellable,
92
                                                           GError         **error);
93
94
static void     g_memory_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
95
static gboolean g_memory_input_stream_is_readable         (GPollableInputStream *stream);
96
static GSource *g_memory_input_stream_create_source       (GPollableInputStream *stream,
97
                 GCancellable          *cancellable);
98
99
static void     g_memory_input_stream_finalize            (GObject         *object);
100
101
G_DEFINE_TYPE_WITH_CODE (GMemoryInputStream, g_memory_input_stream, G_TYPE_INPUT_STREAM,
102
                         G_ADD_PRIVATE (GMemoryInputStream)
103
                         G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
104
                                                g_memory_input_stream_seekable_iface_init);
105
                         G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
106
                                                g_memory_input_stream_pollable_iface_init);
107
       )
108
109
110
static void
111
g_memory_input_stream_class_init (GMemoryInputStreamClass *klass)
112
0
{
113
0
  GObjectClass *object_class;
114
0
  GInputStreamClass *istream_class;
115
116
0
  object_class = G_OBJECT_CLASS (klass);
117
0
  object_class->finalize     = g_memory_input_stream_finalize;
118
  
119
0
  istream_class = G_INPUT_STREAM_CLASS (klass);
120
0
  istream_class->read_fn  = g_memory_input_stream_read;
121
0
  istream_class->skip  = g_memory_input_stream_skip;
122
0
  istream_class->close_fn = g_memory_input_stream_close;
123
124
0
  istream_class->skip_async  = g_memory_input_stream_skip_async;
125
0
  istream_class->skip_finish  = g_memory_input_stream_skip_finish;
126
0
  istream_class->close_async = g_memory_input_stream_close_async;
127
0
  istream_class->close_finish = g_memory_input_stream_close_finish;
128
0
}
129
130
static void
131
g_memory_input_stream_finalize (GObject *object)
132
0
{
133
0
  GMemoryInputStream        *stream;
134
0
  GMemoryInputStreamPrivate *priv;
135
136
0
  stream = G_MEMORY_INPUT_STREAM (object);
137
0
  priv = stream->priv;
138
139
0
  g_slist_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref);
140
141
0
  G_OBJECT_CLASS (g_memory_input_stream_parent_class)->finalize (object);
142
0
}
143
144
static void
145
g_memory_input_stream_seekable_iface_init (GSeekableIface *iface)
146
0
{
147
0
  iface->tell         = g_memory_input_stream_tell;
148
0
  iface->can_seek     = g_memory_input_stream_can_seek;
149
0
  iface->seek         = g_memory_input_stream_seek;
150
0
  iface->can_truncate = g_memory_input_stream_can_truncate;
151
0
  iface->truncate_fn  = g_memory_input_stream_truncate;
152
0
}
153
154
static void
155
g_memory_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
156
0
{
157
0
  iface->is_readable   = g_memory_input_stream_is_readable;
158
0
  iface->create_source = g_memory_input_stream_create_source;
159
0
}
160
161
static void
162
g_memory_input_stream_init (GMemoryInputStream *stream)
163
0
{
164
0
  stream->priv = g_memory_input_stream_get_instance_private (stream);
165
0
}
166
167
/**
168
 * g_memory_input_stream_new:
169
 *
170
 * Creates a new empty #GMemoryInputStream. 
171
 *
172
 * Returns: a new #GInputStream
173
 */
174
GInputStream *
175
g_memory_input_stream_new (void)
176
0
{
177
0
  GInputStream *stream;
178
179
0
  stream = g_object_new (G_TYPE_MEMORY_INPUT_STREAM, NULL);
180
181
0
  return stream;
182
0
}
183
184
/**
185
 * g_memory_input_stream_new_from_data:
186
 * @data: (array length=len) (element-type guint8) (transfer full): input data
187
 * @len: length of the data, may be -1 if @data is a nul-terminated string
188
 * @destroy: (nullable): function that is called to free @data, or %NULL
189
 *
190
 * Creates a new #GMemoryInputStream with data in memory of a given size.
191
 * 
192
 * Returns: new #GInputStream read from @data of @len bytes.
193
 **/
194
GInputStream *
195
g_memory_input_stream_new_from_data (const void     *data, 
196
                                     gssize          len,
197
                                     GDestroyNotify  destroy)
198
0
{
199
0
  GInputStream *stream;
200
201
0
  stream = g_memory_input_stream_new ();
202
203
0
  g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
204
0
                                  data, len, destroy);
205
206
0
  return stream;
207
0
}
208
209
/**
210
 * g_memory_input_stream_new_from_bytes:
211
 * @bytes: a #GBytes
212
 *
213
 * Creates a new #GMemoryInputStream with data from the given @bytes.
214
 *
215
 * Returns: new #GInputStream read from @bytes
216
 *
217
 * Since: 2.34
218
 **/
219
GInputStream *
220
g_memory_input_stream_new_from_bytes (GBytes  *bytes)
221
0
{
222
  
223
0
  GInputStream *stream;
224
225
0
  stream = g_memory_input_stream_new ();
226
227
0
  g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream),
228
0
           bytes);
229
230
0
  return stream;
231
0
}
232
233
/**
234
 * g_memory_input_stream_add_data:
235
 * @stream: a #GMemoryInputStream
236
 * @data: (array length=len) (element-type guint8) (transfer full): input data
237
 * @len: length of the data, may be -1 if @data is a nul-terminated string
238
 * @destroy: (nullable): function that is called to free @data, or %NULL
239
 *
240
 * Appends @data to data that can be read from the input stream
241
 */
242
void
243
g_memory_input_stream_add_data (GMemoryInputStream *stream,
244
                                const void         *data,
245
                                gssize              len,
246
                                GDestroyNotify      destroy)
247
0
{
248
0
  GBytes *bytes;
249
250
0
  if (len == -1)
251
0
    len = strlen (data);
252
253
  /* It's safe to discard the const here because we're chaining the
254
   * destroy callback.
255
   */
256
0
  bytes = g_bytes_new_with_free_func (data, len, destroy, (void*)data);
257
258
0
  g_memory_input_stream_add_bytes (stream, bytes);
259
  
260
0
  g_bytes_unref (bytes);
261
0
}
262
263
/**
264
 * g_memory_input_stream_add_bytes:
265
 * @stream: a #GMemoryInputStream
266
 * @bytes: input data
267
 *
268
 * Appends @bytes to data that can be read from the input stream.
269
 *
270
 * Since: 2.34
271
 */
272
void
273
g_memory_input_stream_add_bytes (GMemoryInputStream *stream,
274
         GBytes             *bytes)
275
0
{
276
0
  GMemoryInputStreamPrivate *priv;
277
 
278
0
  g_return_if_fail (G_IS_MEMORY_INPUT_STREAM (stream));
279
0
  g_return_if_fail (bytes != NULL);
280
281
0
  priv = stream->priv;
282
283
0
  priv->chunks = g_slist_append (priv->chunks, g_bytes_ref (bytes));
284
0
  priv->len += g_bytes_get_size (bytes);
285
0
}
286
287
static gssize
288
g_memory_input_stream_read (GInputStream  *stream,
289
                            void          *buffer,
290
                            gsize          count,
291
                            GCancellable  *cancellable,
292
                            GError       **error)
293
0
{
294
0
  GMemoryInputStream *memory_stream;
295
0
  GMemoryInputStreamPrivate *priv;
296
0
  GSList *l;
297
0
  GBytes *chunk;
298
0
  gsize len;
299
0
  gsize offset, start, rest, size;
300
301
0
  memory_stream = G_MEMORY_INPUT_STREAM (stream);
302
0
  priv = memory_stream->priv;
303
304
0
  count = MIN (count, priv->len - priv->pos);
305
306
0
  offset = 0;
307
0
  for (l = priv->chunks; l; l = l->next) 
308
0
    {
309
0
      chunk = (GBytes *)l->data;
310
0
      len = g_bytes_get_size (chunk);
311
312
0
      if (offset + len > priv->pos)
313
0
        break;
314
315
0
      offset += len;
316
0
    }
317
  
318
0
  start = priv->pos - offset;
319
0
  rest = count;
320
321
0
  for (; l && rest > 0; l = l->next)
322
0
    {
323
0
      const guint8* chunk_data;
324
0
      chunk = (GBytes *)l->data;
325
326
0
      chunk_data = g_bytes_get_data (chunk, &len);
327
328
0
      size = MIN (rest, len - start);
329
330
0
      memcpy ((guint8 *)buffer + (count - rest), chunk_data + start, size);
331
0
      rest -= size;
332
333
0
      start = 0;
334
0
    }
335
336
0
  priv->pos += count;
337
338
0
  return count;
339
0
}
340
341
static gssize
342
g_memory_input_stream_skip (GInputStream  *stream,
343
                            gsize          count,
344
                            GCancellable  *cancellable,
345
                            GError       **error)
346
0
{
347
0
  GMemoryInputStream *memory_stream;
348
0
  GMemoryInputStreamPrivate *priv;
349
350
0
  memory_stream = G_MEMORY_INPUT_STREAM (stream);
351
0
  priv = memory_stream->priv;
352
353
0
  count = MIN (count, priv->len - priv->pos);
354
0
  priv->pos += count;
355
356
0
  return count;
357
0
}
358
359
static gboolean
360
g_memory_input_stream_close (GInputStream  *stream,
361
                             GCancellable  *cancellable,
362
                             GError       **error)
363
0
{
364
0
  return TRUE;
365
0
}
366
367
static void
368
g_memory_input_stream_skip_async (GInputStream        *stream,
369
                                  gsize                count,
370
                                  int                  io_priority,
371
                                  GCancellable        *cancellable,
372
                                  GAsyncReadyCallback  callback,
373
                                  gpointer             user_data)
374
0
{
375
0
  GTask *task;
376
0
  gssize nskipped;
377
0
  GError *error = NULL;
378
379
0
  nskipped = G_INPUT_STREAM_GET_CLASS (stream)->skip (stream, count, cancellable, &error);
380
0
  task = g_task_new (stream, cancellable, callback, user_data);
381
0
  g_task_set_source_tag (task, g_memory_input_stream_skip_async);
382
383
0
  if (error)
384
0
    g_task_return_error (task, error);
385
0
  else
386
0
    g_task_return_int (task, nskipped);
387
0
  g_object_unref (task);
388
0
}
389
390
static gssize
391
g_memory_input_stream_skip_finish (GInputStream  *stream,
392
                                   GAsyncResult  *result,
393
                                   GError       **error)
394
0
{
395
0
  g_return_val_if_fail (g_task_is_valid (result, stream), -1);
396
397
0
  return g_task_propagate_int (G_TASK (result), error);
398
0
}
399
400
static void
401
g_memory_input_stream_close_async (GInputStream        *stream,
402
                                   int                  io_priority,
403
                                   GCancellable        *cancellable,
404
                                   GAsyncReadyCallback  callback,
405
                                   gpointer             user_data)
406
0
{
407
0
  GTask *task;
408
409
0
  task = g_task_new (stream, cancellable, callback, user_data);
410
0
  g_task_set_source_tag (task, g_memory_input_stream_close_async);
411
0
  g_task_return_boolean (task, TRUE);
412
0
  g_object_unref (task);
413
0
}
414
415
static gboolean
416
g_memory_input_stream_close_finish (GInputStream  *stream,
417
                                    GAsyncResult  *result,
418
                                    GError       **error)
419
0
{
420
0
  return TRUE;
421
0
}
422
423
static goffset
424
g_memory_input_stream_tell (GSeekable *seekable)
425
0
{
426
0
  GMemoryInputStream *memory_stream;
427
0
  GMemoryInputStreamPrivate *priv;
428
429
0
  memory_stream = G_MEMORY_INPUT_STREAM (seekable);
430
0
  priv = memory_stream->priv;
431
432
0
  return priv->pos;
433
0
}
434
435
static
436
gboolean g_memory_input_stream_can_seek (GSeekable *seekable)
437
0
{
438
0
  return TRUE;
439
0
}
440
441
static gboolean
442
g_memory_input_stream_seek (GSeekable     *seekable,
443
                            goffset        offset,
444
                            GSeekType      type,
445
                            GCancellable  *cancellable,
446
                            GError       **error)
447
0
{
448
0
  GMemoryInputStream *memory_stream;
449
0
  GMemoryInputStreamPrivate *priv;
450
0
  goffset absolute;
451
452
0
  memory_stream = G_MEMORY_INPUT_STREAM (seekable);
453
0
  priv = memory_stream->priv;
454
455
0
  switch (type) 
456
0
    {
457
0
    case G_SEEK_CUR:
458
0
      absolute = priv->pos + offset;
459
0
      break;
460
461
0
    case G_SEEK_SET:
462
0
      absolute = offset;
463
0
      break;
464
465
0
    case G_SEEK_END:
466
0
      absolute = priv->len + offset;
467
0
      break;
468
  
469
0
    default:
470
0
      g_set_error_literal (error,
471
0
                           G_IO_ERROR,
472
0
                           G_IO_ERROR_INVALID_ARGUMENT,
473
0
                           _("Invalid GSeekType supplied"));
474
475
0
      return FALSE;
476
0
    }
477
478
0
  if (absolute < 0 || (gsize) absolute > priv->len)
479
0
    {
480
0
      g_set_error_literal (error,
481
0
                           G_IO_ERROR,
482
0
                           G_IO_ERROR_INVALID_ARGUMENT,
483
0
                           _("Invalid seek request"));
484
0
      return FALSE;
485
0
    }
486
487
0
  priv->pos = absolute;
488
489
0
  return TRUE;
490
0
}
491
492
static gboolean
493
g_memory_input_stream_can_truncate (GSeekable *seekable)
494
0
{
495
0
  return FALSE;
496
0
}
497
498
static gboolean
499
g_memory_input_stream_truncate (GSeekable     *seekable,
500
                                goffset        offset,
501
                                GCancellable  *cancellable,
502
                                GError       **error)
503
0
{
504
0
  g_set_error_literal (error,
505
0
                       G_IO_ERROR,
506
0
                       G_IO_ERROR_NOT_SUPPORTED,
507
0
                       _("Cannot truncate GMemoryInputStream"));
508
0
  return FALSE;
509
0
}
510
511
static gboolean
512
g_memory_input_stream_is_readable (GPollableInputStream *stream)
513
0
{
514
0
  return TRUE;
515
0
}
516
517
static GSource *
518
g_memory_input_stream_create_source (GPollableInputStream *stream,
519
             GCancellable         *cancellable)
520
0
{
521
0
  GSource *base_source, *pollable_source;
522
523
0
  base_source = g_timeout_source_new (0);
524
0
  pollable_source = g_pollable_source_new_full (stream, base_source,
525
0
            cancellable);
526
0
  g_source_unref (base_source);
527
528
0
  return pollable_source;
529
0
}