Coverage Report

Created: 2025-07-01 07:09

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