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 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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Author: Christian Kellner <gicmo@gnome.org>
21 */
22
23 #include "config.h"
24 #include "gmemoryoutputstream.h"
25 #include "goutputstream.h"
26 #include "gseekable.h"
27 #include "gsimpleasyncresult.h"
28 #include "gioerror.h"
29 #include "string.h"
30 #include "glibintl.h"
31
32 #include "gioalias.h"
33
34 /**
35 * SECTION:gmemoryoutputstream
36 * @short_description: Streaming output operations on memory chunks
37 * @include: gio/gio.h
38 * @see_also: #GMemoryInputStream
39 *
40 * #GMemoryOutputStream is a class for using arbitrary
41 * memory chunks as output for GIO streaming output operations.
42 *
43 */
44
45 #define MIN_ARRAY_SIZE 16
46
47 struct _GMemoryOutputStreamPrivate {
48
49 gpointer data;
50 gsize len;
51 gsize valid_len; /* The part of data that has been written to */
52
53 goffset pos;
54
55 GReallocFunc realloc_fn;
56 GDestroyNotify destroy;
57 };
58
59 static void g_memory_output_stream_finalize (GObject *object);
60
61 static gssize g_memory_output_stream_write (GOutputStream *stream,
62 const void *buffer,
63 gsize count,
64 GCancellable *cancellable,
65 GError **error);
66
67 static gboolean g_memory_output_stream_close (GOutputStream *stream,
68 GCancellable *cancellable,
69 GError **error);
70
71 static void g_memory_output_stream_write_async (GOutputStream *stream,
72 const void *buffer,
73 gsize count,
74 int io_priority,
75 GCancellable *cancellable,
76 GAsyncReadyCallback callback,
77 gpointer data);
78 static gssize g_memory_output_stream_write_finish (GOutputStream *stream,
79 GAsyncResult *result,
80 GError **error);
81 static void g_memory_output_stream_close_async (GOutputStream *stream,
82 int io_priority,
83 GCancellable *cancellable,
84 GAsyncReadyCallback callback,
85 gpointer data);
86 static gboolean g_memory_output_stream_close_finish (GOutputStream *stream,
87 GAsyncResult *result,
88 GError **error);
89
90 static void g_memory_output_stream_seekable_iface_init (GSeekableIface *iface);
91 static goffset g_memory_output_stream_tell (GSeekable *seekable);
92 static gboolean g_memory_output_stream_can_seek (GSeekable *seekable);
93 static gboolean g_memory_output_stream_seek (GSeekable *seekable,
94 goffset offset,
95 GSeekType type,
96 GCancellable *cancellable,
97 GError **error);
98 static gboolean g_memory_output_stream_can_truncate (GSeekable *seekable);
99 static gboolean g_memory_output_stream_truncate (GSeekable *seekable,
100 goffset offset,
101 GCancellable *cancellable,
102 GError **error);
103
G_DEFINE_TYPE_WITH_CODE(GMemoryOutputStream,g_memory_output_stream,G_TYPE_OUTPUT_STREAM,G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,g_memory_output_stream_seekable_iface_init))104 G_DEFINE_TYPE_WITH_CODE (GMemoryOutputStream, g_memory_output_stream, G_TYPE_OUTPUT_STREAM,
105 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
106 g_memory_output_stream_seekable_iface_init))
107
108
109 static void
110 g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass)
111 {
112 GOutputStreamClass *ostream_class;
113 GObjectClass *gobject_class;
114
115 g_type_class_add_private (klass, sizeof (GMemoryOutputStreamPrivate));
116
117 gobject_class = G_OBJECT_CLASS (klass);
118 gobject_class->finalize = g_memory_output_stream_finalize;
119
120 ostream_class = G_OUTPUT_STREAM_CLASS (klass);
121
122 ostream_class->write_fn = g_memory_output_stream_write;
123 ostream_class->close_fn = g_memory_output_stream_close;
124 ostream_class->write_async = g_memory_output_stream_write_async;
125 ostream_class->write_finish = g_memory_output_stream_write_finish;
126 ostream_class->close_async = g_memory_output_stream_close_async;
127 ostream_class->close_finish = g_memory_output_stream_close_finish;
128 }
129
130 static void
g_memory_output_stream_finalize(GObject * object)131 g_memory_output_stream_finalize (GObject *object)
132 {
133 GMemoryOutputStream *stream;
134 GMemoryOutputStreamPrivate *priv;
135
136 stream = G_MEMORY_OUTPUT_STREAM (object);
137 priv = stream->priv;
138
139 if (priv->destroy)
140 priv->destroy (priv->data);
141
142 G_OBJECT_CLASS (g_memory_output_stream_parent_class)->finalize (object);
143 }
144
145 static void
g_memory_output_stream_seekable_iface_init(GSeekableIface * iface)146 g_memory_output_stream_seekable_iface_init (GSeekableIface *iface)
147 {
148 iface->tell = g_memory_output_stream_tell;
149 iface->can_seek = g_memory_output_stream_can_seek;
150 iface->seek = g_memory_output_stream_seek;
151 iface->can_truncate = g_memory_output_stream_can_truncate;
152 iface->truncate_fn = g_memory_output_stream_truncate;
153 }
154
155
156 static void
g_memory_output_stream_init(GMemoryOutputStream * stream)157 g_memory_output_stream_init (GMemoryOutputStream *stream)
158 {
159 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
160 G_TYPE_MEMORY_OUTPUT_STREAM,
161 GMemoryOutputStreamPrivate);
162 }
163
164 /**
165 * g_memory_output_stream_new:
166 * @data: pointer to a chunk of memory to use, or %NULL
167 * @len: the size of @data
168 * @realloc_fn: a function with realloc() semantics to be called when
169 * @data needs to be grown, or %NULL
170 * @destroy: a function to be called on @data when the stream is finalized,
171 * or %NULL
172 *
173 * Creates a new #GMemoryOutputStream.
174 *
175 * If @data is non-%NULL, the stream will use that for its internal storage.
176 * If @realloc_fn is non-%NULL, it will be used for resizing the internal
177 * storage when necessary. To construct a fixed-size output stream,
178 * pass %NULL as @realloc_fn.
179 * |[
180 * /* a stream that can grow */
181 * stream = g_memory_output_stream_new (NULL, 0, realloc, free);
182 *
183 * /* a fixed-size stream */
184 * data = malloc (200);
185 * stream2 = g_memory_output_stream_new (data, 200, NULL, free);
186 * ]|
187 *
188 * Return value: A newly created #GMemoryOutputStream object.
189 **/
190 GOutputStream *
g_memory_output_stream_new(gpointer data,gsize len,GReallocFunc realloc_fn,GDestroyNotify destroy)191 g_memory_output_stream_new (gpointer data,
192 gsize len,
193 GReallocFunc realloc_fn,
194 GDestroyNotify destroy)
195 {
196 GOutputStream *stream;
197 GMemoryOutputStreamPrivate *priv;
198
199 stream = g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, NULL);
200
201 priv = G_MEMORY_OUTPUT_STREAM (stream)->priv;
202
203 priv->data = data;
204 priv->len = len;
205 priv->realloc_fn = realloc_fn;
206 priv->destroy = destroy;
207
208 priv->pos = 0;
209 priv->valid_len = 0;
210
211 return stream;
212 }
213
214 /**
215 * g_memory_output_stream_get_data:
216 * @ostream: a #GMemoryOutputStream
217 *
218 * Gets any loaded data from the @ostream.
219 *
220 * Note that the returned pointer may become invalid on the next
221 * write or truncate operation on the stream.
222 *
223 * Returns: pointer to the stream's data
224 **/
225 gpointer
g_memory_output_stream_get_data(GMemoryOutputStream * ostream)226 g_memory_output_stream_get_data (GMemoryOutputStream *ostream)
227 {
228 g_return_val_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream), NULL);
229
230 return ostream->priv->data;
231 }
232
233 /**
234 * g_memory_output_stream_get_size:
235 * @ostream: a #GMemoryOutputStream
236 *
237 * Gets the size of the currently allocated data area (availible from
238 * g_memory_output_stream_get_data()). If the stream isn't
239 * growable (no realloc was passed to g_memory_output_stream_new()) then
240 * this is the maximum size of the stream and further writes
241 * will return %G_IO_ERROR_NO_SPACE.
242 *
243 * Note that for growable streams the returned size may become invalid on
244 * the next write or truncate operation on the stream.
245 *
246 * If you want the number of bytes currently written to the stream, use
247 * g_memory_output_stream_get_data_size().
248 *
249 * Returns: the number of bytes allocated for the data buffer
250 */
251 gsize
g_memory_output_stream_get_size(GMemoryOutputStream * ostream)252 g_memory_output_stream_get_size (GMemoryOutputStream *ostream)
253 {
254 g_return_val_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream), 0);
255
256 return ostream->priv->len;
257 }
258
259 /**
260 * g_memory_output_stream_get_data_size:
261 * @ostream: a #GMemoryOutputStream
262 *
263 * Returns the number of bytes from the start up
264 * to including the last byte written in the stream
265 * that has not been truncated away.
266 *
267 * Returns: the number of bytes written to the stream
268 *
269 * Since: 2.18
270 */
271 gsize
g_memory_output_stream_get_data_size(GMemoryOutputStream * ostream)272 g_memory_output_stream_get_data_size (GMemoryOutputStream *ostream)
273 {
274 g_return_val_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream), 0);
275
276 return ostream->priv->valid_len;
277 }
278
279
280 static gboolean
array_check_boundary(GMemoryOutputStream * stream,goffset size,GError ** error)281 array_check_boundary (GMemoryOutputStream *stream,
282 goffset size,
283 GError **error)
284 {
285 if (size > G_MAXUINT)
286 {
287 g_set_error_literal (error,
288 G_IO_ERROR,
289 G_IO_ERROR_FAILED,
290 _("Reached maximum data array limit"));
291
292 return FALSE;
293 }
294
295 return TRUE;
296 }
297
298 static gboolean
array_resize(GMemoryOutputStream * ostream,gsize size,gboolean allow_partial,GError ** error)299 array_resize (GMemoryOutputStream *ostream,
300 gsize size,
301 gboolean allow_partial,
302 GError **error)
303 {
304 GMemoryOutputStreamPrivate *priv;
305 gpointer data;
306 gsize len;
307
308 priv = ostream->priv;
309
310 if (!array_check_boundary (ostream, size, error))
311 return FALSE;
312
313 if (priv->len == size)
314 return TRUE;
315
316 if (!priv->realloc_fn)
317 {
318 if (allow_partial &&
319 priv->pos < priv->len)
320 return TRUE; /* Short write */
321
322 g_set_error_literal (error,
323 G_IO_ERROR,
324 G_IO_ERROR_NO_SPACE,
325 _("Memory output stream not resizable"));
326 return FALSE;
327 }
328
329 len = priv->len;
330 data = priv->realloc_fn (priv->data, size);
331
332 if (size > 0 && !data)
333 {
334 if (allow_partial &&
335 priv->pos < priv->len)
336 return TRUE; /* Short write */
337
338 g_set_error_literal (error,
339 G_IO_ERROR,
340 G_IO_ERROR_NO_SPACE,
341 _("Failed to resize memory output stream"));
342 return FALSE;
343 }
344
345 if (size > len)
346 memset ((guint8 *)data + len, 0, size - len);
347
348 priv->data = data;
349 priv->len = size;
350
351 if (priv->len < priv->valid_len)
352 priv->valid_len = priv->len;
353
354 return TRUE;
355 }
356
357 static gint
g_nearest_pow(gint num)358 g_nearest_pow (gint num)
359 {
360 gint n = 1;
361
362 while (n < num)
363 n <<= 1;
364
365 return n;
366 }
367
368 static gssize
g_memory_output_stream_write(GOutputStream * stream,const void * buffer,gsize count,GCancellable * cancellable,GError ** error)369 g_memory_output_stream_write (GOutputStream *stream,
370 const void *buffer,
371 gsize count,
372 GCancellable *cancellable,
373 GError **error)
374 {
375 GMemoryOutputStream *ostream;
376 GMemoryOutputStreamPrivate *priv;
377 guint8 *dest;
378 gsize new_size;
379
380 ostream = G_MEMORY_OUTPUT_STREAM (stream);
381 priv = ostream->priv;
382
383 if (count == 0)
384 return 0;
385
386 if (priv->pos + count > priv->len)
387 {
388 /* At least enought to fit the write, rounded up
389 for greater than linear growth */
390 new_size = g_nearest_pow (priv->pos + count);
391 new_size = MAX (new_size, MIN_ARRAY_SIZE);
392
393 if (!array_resize (ostream, new_size, TRUE, error))
394 return -1;
395 }
396
397 /* Make sure we handle short writes if the array_resize
398 only added part of the required memory */
399 count = MIN (count, priv->len - priv->pos);
400
401 dest = (guint8 *)priv->data + priv->pos;
402 memcpy (dest, buffer, count);
403 priv->pos += count;
404
405 if (priv->pos > priv->valid_len)
406 priv->valid_len = priv->pos;
407
408 return count;
409 }
410
411 static gboolean
g_memory_output_stream_close(GOutputStream * stream,GCancellable * cancellable,GError ** error)412 g_memory_output_stream_close (GOutputStream *stream,
413 GCancellable *cancellable,
414 GError **error)
415 {
416 return TRUE;
417 }
418
419 static void
g_memory_output_stream_write_async(GOutputStream * stream,const void * buffer,gsize count,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)420 g_memory_output_stream_write_async (GOutputStream *stream,
421 const void *buffer,
422 gsize count,
423 int io_priority,
424 GCancellable *cancellable,
425 GAsyncReadyCallback callback,
426 gpointer data)
427 {
428 GSimpleAsyncResult *simple;
429 gssize nwritten;
430
431 nwritten = g_memory_output_stream_write (stream,
432 buffer,
433 count,
434 cancellable,
435 NULL);
436
437
438 simple = g_simple_async_result_new (G_OBJECT (stream),
439 callback,
440 data,
441 g_memory_output_stream_write_async);
442
443 g_simple_async_result_set_op_res_gssize (simple, nwritten);
444 g_simple_async_result_complete_in_idle (simple);
445 g_object_unref (simple);
446 }
447
448 static gssize
g_memory_output_stream_write_finish(GOutputStream * stream,GAsyncResult * result,GError ** error)449 g_memory_output_stream_write_finish (GOutputStream *stream,
450 GAsyncResult *result,
451 GError **error)
452 {
453 GSimpleAsyncResult *simple;
454 gssize nwritten;
455
456 simple = G_SIMPLE_ASYNC_RESULT (result);
457
458 g_warn_if_fail (g_simple_async_result_get_source_tag (simple) ==
459 g_memory_output_stream_write_async);
460
461 nwritten = g_simple_async_result_get_op_res_gssize (simple);
462
463 return nwritten;
464 }
465
466 static void
g_memory_output_stream_close_async(GOutputStream * stream,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)467 g_memory_output_stream_close_async (GOutputStream *stream,
468 int io_priority,
469 GCancellable *cancellable,
470 GAsyncReadyCallback callback,
471 gpointer data)
472 {
473 GSimpleAsyncResult *simple;
474
475 simple = g_simple_async_result_new (G_OBJECT (stream),
476 callback,
477 data,
478 g_memory_output_stream_close_async);
479
480
481 /* will always return TRUE */
482 g_memory_output_stream_close (stream, cancellable, NULL);
483
484 g_simple_async_result_complete_in_idle (simple);
485 g_object_unref (simple);
486 }
487
488 static gboolean
g_memory_output_stream_close_finish(GOutputStream * stream,GAsyncResult * result,GError ** error)489 g_memory_output_stream_close_finish (GOutputStream *stream,
490 GAsyncResult *result,
491 GError **error)
492 {
493 GSimpleAsyncResult *simple;
494
495 simple = G_SIMPLE_ASYNC_RESULT (result);
496
497 g_warn_if_fail (g_simple_async_result_get_source_tag (simple) ==
498 g_memory_output_stream_close_async);
499
500 return TRUE;
501 }
502
503 static goffset
g_memory_output_stream_tell(GSeekable * seekable)504 g_memory_output_stream_tell (GSeekable *seekable)
505 {
506 GMemoryOutputStream *stream;
507 GMemoryOutputStreamPrivate *priv;
508
509 stream = G_MEMORY_OUTPUT_STREAM (seekable);
510 priv = stream->priv;
511
512 return priv->pos;
513 }
514
515 static gboolean
g_memory_output_stream_can_seek(GSeekable * seekable)516 g_memory_output_stream_can_seek (GSeekable *seekable)
517 {
518 return TRUE;
519 }
520
521 static gboolean
g_memory_output_stream_seek(GSeekable * seekable,goffset offset,GSeekType type,GCancellable * cancellable,GError ** error)522 g_memory_output_stream_seek (GSeekable *seekable,
523 goffset offset,
524 GSeekType type,
525 GCancellable *cancellable,
526 GError **error)
527 {
528 GMemoryOutputStream *stream;
529 GMemoryOutputStreamPrivate *priv;
530 goffset absolute;
531
532 stream = G_MEMORY_OUTPUT_STREAM (seekable);
533 priv = stream->priv;
534
535 switch (type)
536 {
537 case G_SEEK_CUR:
538 absolute = priv->pos + offset;
539 break;
540
541 case G_SEEK_SET:
542 absolute = offset;
543 break;
544
545 case G_SEEK_END:
546 absolute = priv->len + offset;
547 break;
548
549 default:
550 g_set_error_literal (error,
551 G_IO_ERROR,
552 G_IO_ERROR_INVALID_ARGUMENT,
553 _("Invalid GSeekType supplied"));
554
555 return FALSE;
556 }
557
558 if (absolute < 0)
559 {
560 g_set_error_literal (error,
561 G_IO_ERROR,
562 G_IO_ERROR_INVALID_ARGUMENT,
563 _("Invalid seek request"));
564 return FALSE;
565 }
566
567 if (!array_check_boundary (stream, absolute, error))
568 return FALSE;
569
570 priv->pos = absolute;
571
572 return TRUE;
573 }
574
575 static gboolean
g_memory_output_stream_can_truncate(GSeekable * seekable)576 g_memory_output_stream_can_truncate (GSeekable *seekable)
577 {
578 GMemoryOutputStream *ostream;
579 GMemoryOutputStreamPrivate *priv;
580
581 ostream = G_MEMORY_OUTPUT_STREAM (seekable);
582 priv = ostream->priv;
583
584 return priv->realloc_fn != NULL;
585 }
586
587 static gboolean
g_memory_output_stream_truncate(GSeekable * seekable,goffset offset,GCancellable * cancellable,GError ** error)588 g_memory_output_stream_truncate (GSeekable *seekable,
589 goffset offset,
590 GCancellable *cancellable,
591 GError **error)
592 {
593 GMemoryOutputStream *ostream;
594 GMemoryOutputStreamPrivate *priv;
595
596 ostream = G_MEMORY_OUTPUT_STREAM (seekable);
597 priv = ostream->priv;
598
599 if (!array_resize (ostream, offset, FALSE, error))
600 return FALSE;
601
602 return TRUE;
603 }
604
605 #define __G_MEMORY_OUTPUT_STREAM_C__
606 #include "gioaliasdef.c"
607