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

source code of gtk/subprojects/glib/gio/gmemoryinputstream.c