Coverage Report

Created: 2025-06-13 06:55

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