Bug 540461 – g_memory_output_stream_get_data_size() doesn't behave as
[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 "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
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
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
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
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  * /&ast; a stream that can grow &ast;/
181  * stream = g_memory_output_stream_new (NULL, 0, realloc, free);
182  *
183  * /&ast; a fixed-size stream &ast;/
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 *
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
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
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
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
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
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
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
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
412 g_memory_output_stream_close (GOutputStream  *stream,
413                               GCancellable   *cancellable,
414                               GError        **error)
415 {
416   return TRUE;
417 }
418
419 static void
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
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
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
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
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
516 g_memory_output_stream_can_seek (GSeekable *seekable)
517 {
518   return TRUE;
519 }
520
521 static gboolean
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
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
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"