http://mail.gnome.org/archives/gtk-devel-list/2007-October/msg00089.html
[platform/upstream/glib.git] / gio / gmemoryoutputstream.c
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 "string.h"
29 #include "glibintl.h"
30
31 #include "gioalias.h"
32
33 /**
34  * SECTION:gmemoryoutputstream
35  * @short_description: Streaming output operations on memory chunks
36  * @see_also: #GMemoryInputStream
37  *
38  * #GMemoryOutputStream is a class for using arbitrary
39  * memory chunks as output for GIO streaming output operations.
40  *
41  */
42
43 struct _GMemoryOutputStreamPrivate {
44   
45   GByteArray *data;
46   goffset     pos;
47
48   guint       max_size;
49   guint       free_data : 1;
50 };
51
52 enum {
53   PROP_0,
54   PROP_DATA,
55   PROP_FREE_ARRAY,
56   PROP_SIZE_LIMIT
57 };
58
59 static void     g_memory_output_stream_finalize     (GObject         *object);
60
61 static void     g_memory_output_stream_set_property (GObject      *object,
62                                                      guint         prop_id,
63                                                      const GValue *value,
64                                                      GParamSpec   *pspec);
65
66 static void     g_memory_output_stream_get_property (GObject    *object,
67                                                      guint       prop_id,
68                                                      GValue     *value,
69                                                      GParamSpec *pspec);
70
71 static gssize   g_memory_output_stream_write       (GOutputStream *stream,
72                                                     const void    *buffer,
73                                                     gsize          count,
74                                                     GCancellable  *cancellable,
75                                                     GError       **error);
76
77 static gboolean g_memory_output_stream_close       (GOutputStream  *stream,
78                                                     GCancellable   *cancellable,
79                                                     GError        **error);
80
81 static void     g_memory_output_stream_write_async  (GOutputStream        *stream,
82                                                      const void           *buffer,
83                                                      gsize                 count,
84                                                      int                   io_priority,
85                                                      GCancellable         *cancellable,
86                                                      GAsyncReadyCallback   callback,
87                                                      gpointer              data);
88 static gssize   g_memory_output_stream_write_finish (GOutputStream        *stream,
89                                                      GAsyncResult         *result,
90                                                      GError              **error);
91 static void     g_memory_output_stream_close_async  (GOutputStream        *stream,
92                                                      int                   io_priority,
93                                                      GCancellable         *cancellable,
94                                                      GAsyncReadyCallback   callback,
95                                                      gpointer              data);
96 static gboolean g_memory_output_stream_close_finish (GOutputStream        *stream,
97                                                      GAsyncResult         *result,
98                                                      GError              **error);
99
100 static void     g_memory_output_stream_seekable_iface_init (GSeekableIface  *iface);
101 static goffset  g_memory_output_stream_tell                (GSeekable       *seekable);
102 static gboolean g_memory_output_stream_can_seek            (GSeekable       *seekable);
103 static gboolean g_memory_output_stream_seek                (GSeekable       *seekable,
104                                                            goffset          offset,
105                                                            GSeekType        type,
106                                                            GCancellable    *cancellable,
107                                                            GError         **error);
108 static gboolean g_memory_output_stream_can_truncate        (GSeekable       *seekable);
109 static gboolean g_memory_output_stream_truncate            (GSeekable       *seekable,
110                                                            goffset          offset,
111                                                            GCancellable    *cancellable,
112                                                            GError         **error);
113
114 G_DEFINE_TYPE_WITH_CODE (GMemoryOutputStream, g_memory_output_stream, G_TYPE_OUTPUT_STREAM,
115                          G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
116                                                 g_memory_output_stream_seekable_iface_init))
117
118
119 static void
120 g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass)
121 {
122   GOutputStreamClass *ostream_class;
123   GObjectClass *gobject_class;
124
125   g_type_class_add_private (klass, sizeof (GMemoryOutputStreamPrivate));
126
127   gobject_class = G_OBJECT_CLASS (klass);
128   gobject_class->finalize = g_memory_output_stream_finalize;
129   gobject_class->get_property = g_memory_output_stream_get_property;
130   gobject_class->set_property = g_memory_output_stream_set_property;
131
132   ostream_class = G_OUTPUT_STREAM_CLASS (klass);
133
134   ostream_class->write_fn = g_memory_output_stream_write;
135   ostream_class->close_fn = g_memory_output_stream_close;
136   ostream_class->write_async  = g_memory_output_stream_write_async;
137   ostream_class->write_finish = g_memory_output_stream_write_finish;
138   ostream_class->close_async  = g_memory_output_stream_close_async;
139   ostream_class->close_finish = g_memory_output_stream_close_finish;
140
141   g_object_class_install_property (gobject_class,
142                                    PROP_DATA,
143                                    g_param_spec_pointer ("data",
144                                                          P_("Data byte array"),
145                                                          P_("The byte array used as internal storage."),
146                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | 
147                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
148
149   g_object_class_install_property (gobject_class,
150                                    PROP_FREE_ARRAY,
151                                    g_param_spec_boolean ("free-array",
152                                                          P_("Free array data"),
153                                                          P_("Wether or not the interal array should be free on close."),
154                                                          FALSE,
155                                                          G_PARAM_READWRITE |
156                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
157   g_object_class_install_property (gobject_class,
158                                    PROP_SIZE_LIMIT,
159                                    g_param_spec_uint ("size-limit",
160                                                       P_("Limit"),
161                                                       P_("Maximum amount of bytes that can be written to the stream."),
162                                                       0,
163                                                       G_MAXUINT,
164                                                       0,
165                                                       G_PARAM_READWRITE |
166                                                       G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
167
168
169 }
170
171 static void
172 g_memory_output_stream_finalize (GObject *object)
173 {
174   GMemoryOutputStream        *stream;
175
176   stream = G_MEMORY_OUTPUT_STREAM (object);
177   
178   if (stream->priv->free_data)
179     g_byte_array_free (stream->priv->data, TRUE);
180     
181   if (G_OBJECT_CLASS (g_memory_output_stream_parent_class)->finalize)
182     (*G_OBJECT_CLASS (g_memory_output_stream_parent_class)->finalize) (object);
183 }
184
185 static void
186 g_memory_output_stream_seekable_iface_init (GSeekableIface *iface)
187 {
188   iface->tell         = g_memory_output_stream_tell;
189   iface->can_seek     = g_memory_output_stream_can_seek;
190   iface->seek         = g_memory_output_stream_seek;
191   iface->can_truncate = g_memory_output_stream_can_truncate;
192   iface->truncate_fn     = g_memory_output_stream_truncate;
193 }
194
195
196 static void
197 g_memory_output_stream_init (GMemoryOutputStream *stream)
198 {
199   GMemoryOutputStreamPrivate *priv;
200
201   stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
202                                               G_TYPE_MEMORY_OUTPUT_STREAM,
203                                               GMemoryOutputStreamPrivate);
204
205   priv = stream->priv;
206   priv->data = NULL; 
207 }
208
209 /**
210  * g_memory_output_stream_new:
211  * @data: a #GByteArray.
212  *
213  * Creates a new #GMemoryOutputStream. If @data is non-%NULL it will use
214  * that for its internal storage otherwise it will create a new #GByteArray.
215  * In both cases the internal #GByteArray can later be accessed through the 
216  * "data" property, or with g_memory_output_stream_get_data().
217  *
218  * Note: The new stream will not take ownership of the supplied
219  * @data so you have to free it yourself after use or explicitly
220  * ask for it be freed on close by setting the "free-array" 
221  * property to %TRUE.
222  *
223  * Return value: A newly created #GMemoryOutputStream object.
224  **/
225 GOutputStream *
226 g_memory_output_stream_new (GByteArray *data)
227 {
228   GOutputStream *stream;
229
230   if (data == NULL)
231     stream = g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, NULL);
232   else
233     stream = g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM,
234                            "data", data,
235                            NULL);
236
237   return stream;
238 }
239
240 /**
241  * g_memory_output_stream_set_free_data:
242  * @ostream: a #GMemoryOutputStream.
243  * @free_data: a #gboolean. If %TRUE, frees the data within @stream.
244  * 
245  * Sets if the data within the @stream should be freed when the stream 
246  * is freed. 
247  **/
248 void
249 g_memory_output_stream_set_free_data (GMemoryOutputStream *ostream,
250                                       gboolean             free_data)
251 {
252   GMemoryOutputStreamPrivate *priv;
253
254   g_return_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream));
255
256   priv = ostream->priv;
257
258   priv->free_data = free_data;
259 }
260
261 /**
262  * g_memory_output_stream_set_max_size:
263  * @ostream: a #GMemoryOutputStream.
264  * @max_size: a #guint to set as the maximum stream size.
265  *
266  * Sets a size limit on the data contained within the output stream.
267  **/
268 void
269 g_memory_output_stream_set_max_size (GMemoryOutputStream *ostream,
270                                      guint                max_size)
271 {
272   GMemoryOutputStreamPrivate *priv;
273   
274   g_return_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream));
275
276   priv = ostream->priv;
277
278   priv->max_size = max_size;
279
280   if (priv->max_size > 0 &&
281       priv->max_size < priv->data->len) 
282     {
283
284       g_byte_array_set_size (priv->data, priv->max_size);
285
286       if (priv->pos > priv->max_size) 
287         priv->pos = priv->max_size;
288     }
289
290   g_object_notify (G_OBJECT (ostream), "size-limit");
291 }
292
293 static void
294 g_memory_output_stream_set_property (GObject      *object,
295                                      guint         prop_id,
296                                      const GValue *value,
297                                      GParamSpec   *pspec)
298 {
299   GMemoryOutputStream *ostream;
300   GMemoryOutputStreamPrivate *priv;
301   GByteArray *data;
302   guint       max_size;
303
304   ostream = G_MEMORY_OUTPUT_STREAM (object);
305   priv = ostream->priv;
306
307   switch (prop_id)
308     {
309     case PROP_DATA:
310
311       if (priv->data && priv->free_data) 
312         g_byte_array_free (priv->data, TRUE);
313
314       data = g_value_get_pointer (value);
315
316       if (data == NULL) 
317         {
318           data = g_byte_array_new (); 
319           priv->free_data = TRUE;
320         } 
321       else 
322         priv->free_data = FALSE;
323  
324       priv->data = data;
325       priv->pos  = 0;
326       g_object_notify (G_OBJECT (ostream), "data");
327       break;
328
329     case PROP_FREE_ARRAY:
330       priv->free_data = g_value_get_boolean (value);
331       break;
332
333     case PROP_SIZE_LIMIT:
334       max_size = g_value_get_uint (value);
335       g_memory_output_stream_set_max_size (ostream, max_size);
336       break;
337
338     default:
339       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340       break;
341     }
342 }
343
344 static void
345 g_memory_output_stream_get_property (GObject    *object,
346                                      guint       prop_id,
347                                      GValue     *value,
348                                      GParamSpec *pspec)
349 {
350   GMemoryOutputStream *ostream;
351   GMemoryOutputStreamPrivate *priv;
352
353   ostream = G_MEMORY_OUTPUT_STREAM (object);
354   priv = ostream->priv;
355
356   switch (prop_id)
357     {
358     case PROP_DATA:
359       g_value_set_pointer (value, priv->data);
360       break;
361
362     case PROP_FREE_ARRAY:
363       g_value_set_boolean (value, priv->free_data);
364       break;
365
366     case PROP_SIZE_LIMIT:
367       g_value_set_uint (value, priv->max_size);
368       break;
369
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373     }
374 }
375
376 /**
377  * g_memory_output_stream_get_data:
378  * @ostream: a #GMemoryOutputStream
379  *
380  * Gets any loaded data from the @ostream.
381  * 
382  * Returns: #GByteArray of the stream's data.
383  **/
384 GByteArray *
385 g_memory_output_stream_get_data (GMemoryOutputStream *ostream)
386 {
387   g_return_val_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream), NULL);
388
389   return ostream->priv->data;
390 }
391
392
393 static gboolean
394 array_check_boundary (GMemoryOutputStream  *stream,
395                       goffset               size,
396                       GError              **error)
397 {
398   GMemoryOutputStreamPrivate *priv;
399
400   priv = stream->priv;
401
402   if (!priv->max_size) 
403     return TRUE;
404
405   if (priv->max_size < size || size > G_MAXUINT)
406     {
407       g_set_error (error,
408                    G_IO_ERROR,
409                    G_IO_ERROR_FAILED,
410                    "Reached maximum data array limit");
411
412       return FALSE;
413     }
414
415   return TRUE; 
416 }
417
418 static gssize
419 array_resize (GMemoryOutputStream  *stream,
420               goffset               size,
421               GError              **error)
422 {
423   GMemoryOutputStreamPrivate *priv;
424   guint old_len;
425
426   priv = stream->priv;
427
428   if (! array_check_boundary (stream, size, error)) 
429     return -1;
430   
431
432   if (priv->data->len == size) 
433     return priv->data->len - priv->pos;
434   
435
436   old_len = priv->data->len;
437   g_byte_array_set_size (priv->data, size);
438
439   if (size > old_len && priv->pos > old_len) 
440     memset (priv->data->data + priv->pos, 0, size - old_len);
441
442   return priv->data->len - priv->pos;
443 }
444
445 static gssize
446 g_memory_output_stream_write (GOutputStream  *stream,
447                               const void     *buffer,
448                               gsize           count,
449                               GCancellable   *cancellable,
450                               GError        **error)
451 {
452   GMemoryOutputStream        *ostream;
453   GMemoryOutputStreamPrivate *priv;
454   gsize     new_size;
455   gssize    n;
456   guint8   *dest;
457
458   ostream = G_MEMORY_OUTPUT_STREAM (stream);
459   priv = ostream->priv;
460
461   /* count < 0 is ensured by GOutputStream */
462
463   n = MIN (count, priv->data->len - priv->pos);
464
465   if (n < 1)
466     {
467       new_size = priv->pos + count;
468
469       if (priv->max_size > 0)
470         new_size = MIN (new_size, priv->max_size);
471
472       n = array_resize (ostream, new_size, error);
473
474       if (n == 0) 
475         {
476           g_set_error (error,
477                        G_IO_ERROR,
478                        G_IO_ERROR_FAILED,
479                        "Reached maximum data array limit");
480           return -1;
481         }
482       else if (n < 0)
483         return -1;
484     }
485
486   dest = priv->data->data + priv->pos;
487   memcpy (dest, buffer, n); 
488   priv->pos += n;
489
490   return n;
491 }
492
493 static gboolean
494 g_memory_output_stream_close (GOutputStream  *stream,
495                               GCancellable   *cancellable,
496                               GError        **error)
497 {
498   GMemoryOutputStream        *ostream;
499   GMemoryOutputStreamPrivate *priv;
500
501   ostream = G_MEMORY_OUTPUT_STREAM (stream);
502   priv = ostream->priv;
503
504   return TRUE;
505 }
506
507 static void
508 g_memory_output_stream_write_async  (GOutputStream       *stream,
509                                      const void          *buffer,
510                                      gsize                count,
511                                      int                  io_priority,
512                                      GCancellable        *cancellable,
513                                      GAsyncReadyCallback  callback,
514                                      gpointer             data)
515 {
516   GSimpleAsyncResult *simple;
517   gssize nwritten;
518
519   nwritten = g_memory_output_stream_write (stream,
520                                            buffer,
521                                            count,
522                                            cancellable,
523                                            NULL);
524  
525
526   simple = g_simple_async_result_new (G_OBJECT (stream),
527                                       callback,
528                                       data,
529                                       g_memory_output_stream_write_async);
530   
531   g_simple_async_result_set_op_res_gssize (simple, nwritten); 
532   g_simple_async_result_complete_in_idle (simple);
533   g_object_unref (simple);
534 }
535
536 static gssize
537 g_memory_output_stream_write_finish (GOutputStream  *stream,
538                                      GAsyncResult   *result,
539                                      GError        **error)
540 {
541   GSimpleAsyncResult *simple;
542   gssize nwritten;
543
544   simple = G_SIMPLE_ASYNC_RESULT (result);
545   
546   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == 
547             g_memory_output_stream_write_async);
548
549   nwritten = g_simple_async_result_get_op_res_gssize (simple);
550   return nwritten;
551 }
552
553 static void
554 g_memory_output_stream_close_async  (GOutputStream       *stream,
555                                      int                  io_priority,
556                                      GCancellable        *cancellable,
557                                      GAsyncReadyCallback  callback,
558                                      gpointer             data)
559 {
560   GSimpleAsyncResult *simple;
561
562   simple = g_simple_async_result_new (G_OBJECT (stream),
563                                       callback,
564                                       data,
565                                       g_memory_output_stream_close_async);
566
567
568   /* will always return TRUE */
569   g_memory_output_stream_close (stream, cancellable, NULL);
570   
571   g_simple_async_result_complete_in_idle (simple);
572   g_object_unref (simple);
573 }
574
575 static gboolean
576 g_memory_output_stream_close_finish (GOutputStream  *stream,
577                                      GAsyncResult   *result,
578                                      GError        **error)
579 {
580   GSimpleAsyncResult *simple;
581
582   simple = G_SIMPLE_ASYNC_RESULT (result);
583
584   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == 
585             g_memory_output_stream_close_async);
586
587   return TRUE;
588 }
589
590 static goffset
591 g_memory_output_stream_tell (GSeekable *seekable)
592 {
593   GMemoryOutputStream *stream;
594   GMemoryOutputStreamPrivate *priv;
595
596   stream = G_MEMORY_OUTPUT_STREAM (seekable);
597   priv = stream->priv;
598
599   return priv->pos;
600 }
601
602 static gboolean
603 g_memory_output_stream_can_seek (GSeekable *seekable)
604 {
605   return TRUE;
606 }
607
608 static gboolean
609 g_memory_output_stream_seek (GSeekable    *seekable,
610                             goffset        offset,
611                             GSeekType      type,
612                             GCancellable  *cancellable,
613                             GError       **error)
614 {
615   GMemoryOutputStream        *stream;
616   GMemoryOutputStreamPrivate *priv;
617   goffset absolute;
618
619   stream = G_MEMORY_OUTPUT_STREAM (seekable);
620   priv = stream->priv;
621
622   switch (type) 
623     {
624     case G_SEEK_CUR:
625       absolute = priv->pos + offset;
626       break;
627
628     case G_SEEK_SET:
629       absolute = offset;
630       break;
631
632     case G_SEEK_END:
633       absolute = priv->data->len + offset;
634       break;
635   
636     default:
637       g_set_error (error,
638                    G_IO_ERROR,
639                    G_IO_ERROR_INVALID_ARGUMENT,
640                    "Invalid GSeekType supplied");
641
642       return FALSE;
643     }
644
645   if (absolute < 0) 
646     {
647       g_set_error (error,
648                    G_IO_ERROR,
649                    G_IO_ERROR_INVALID_ARGUMENT,
650                    "Invalid seek request");
651       return FALSE;
652     }
653
654   if (!array_check_boundary (stream, absolute, error)) 
655     return FALSE;  
656
657   priv->pos = absolute;
658
659   return TRUE;
660 }
661
662 static gboolean
663 g_memory_output_stream_can_truncate (GSeekable *seekable)
664 {
665   return TRUE;
666 }
667
668 static gboolean
669 g_memory_output_stream_truncate (GSeekable     *seekable,
670                                  goffset        offset,
671                                  GCancellable  *cancellable,
672                                  GError       **error)
673 {
674   GMemoryOutputStream *ostream;
675   GMemoryOutputStreamPrivate *priv;
676
677   ostream = G_MEMORY_OUTPUT_STREAM (seekable);
678   priv = ostream->priv;
679  
680   if (array_resize (ostream, offset, error) < 0)
681     return FALSE;
682
683   return TRUE;
684 }
685
686 #define __G_MEMORY_OUTPUT_STREAM_C__
687 #include "gioaliasdef.c"