"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-output-memory.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-output-memory.c:
4  *
5  * Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 #include <gsf-config.h>
22 #include <gsf/gsf-output-memory.h>
23 #include <gsf/gsf-output-impl.h>
24 #include <gsf/gsf-impl-utils.h>
25 #include <string.h>
26
27 #define MIN_BLOCK 512
28 #define MAX_STEP  (MIN_BLOCK * 128)
29
30 static GsfOutputClass *parent_class;
31
32 struct _GsfOutputMemory {
33         GsfOutput output;
34         guint8 *buffer;
35         size_t capacity;
36 };
37
38 typedef struct {
39         GsfOutputClass output_class;
40 } GsfOutputMemoryClass;
41
42 /**
43  * gsf_output_memory_new :
44  *
45  * Returns: a new file or %NULL.
46  **/
47 GsfOutput *
48 gsf_output_memory_new (void)
49 {
50         return g_object_new (GSF_OUTPUT_MEMORY_TYPE, NULL);     
51 }
52
53 static gboolean
54 gsf_output_memory_close (GsfOutput *output)
55 {
56         return (parent_class->Close == NULL) ||
57                 parent_class->Close (output);
58 }
59
60 static void
61 gsf_output_memory_finalize (GObject *obj)
62 {
63         GsfOutputMemory *mem = GSF_OUTPUT_MEMORY (obj);
64         
65         g_free (mem->buffer);
66         mem->buffer = NULL;
67
68         G_OBJECT_CLASS (parent_class)->finalize (obj);
69 }
70
71 static gboolean
72 gsf_output_memory_seek (G_GNUC_UNUSED GsfOutput *output,
73                         G_GNUC_UNUSED gsf_off_t offset,
74                         G_GNUC_UNUSED GSeekType whence)
75 {
76         /* let parent implementation handle maneuvering cur_offset */
77         return TRUE;
78 }
79
80 static gboolean
81 gsf_output_memory_expand (GsfOutputMemory *mem, gsf_off_t needed)
82 {
83         gsf_off_t capacity = MAX (mem->capacity, MIN_BLOCK);
84         gsize lcapacity;
85         
86         /* If we need >= MAX_STEP, align to a next multiple of MAX_STEP.
87          * Since MAX_STEP is probably a power of two, this computation
88          * should reduce to "dec, shr, inc, shl", which is probably
89          * quicker then branching.
90          */
91         if (needed < MAX_STEP)
92                 while (capacity < needed)
93                         capacity *= 2;
94         else
95                 capacity = ((needed - 1) / MAX_STEP + 1) * MAX_STEP;
96
97         /* Check for overflow: g_renew() casts its parameters to gsize. */
98         lcapacity = capacity;
99         if ((gsf_off_t) lcapacity != capacity || capacity < 0) {
100                 g_warning ("overflow in gsf_output_memory_expand");
101                 return FALSE;
102         }
103         mem->buffer   = g_renew (guint8, mem->buffer, lcapacity);
104         mem->capacity = capacity;
105         
106         return TRUE;
107 }
108
109 static gboolean
110 gsf_output_memory_write (GsfOutput *output,
111                          size_t num_bytes,
112                          guint8 const *buffer)
113 {
114         GsfOutputMemory *mem = GSF_OUTPUT_MEMORY (output);
115         
116         g_return_val_if_fail (mem != NULL, FALSE);
117         
118         if (!mem->buffer) {
119                 mem->buffer   = g_new (guint8, MIN_BLOCK);
120                 mem->capacity = MIN_BLOCK;
121         }
122         if (num_bytes + output->cur_offset > mem->capacity) {
123                 if (!gsf_output_memory_expand (mem, output->cur_offset + num_bytes))
124                         return FALSE;
125         }
126         
127         memcpy (mem->buffer + output->cur_offset, buffer, num_bytes);   
128         return TRUE;
129 }
130
131 static gsf_off_t gsf_output_memory_vprintf (GsfOutput *output,
132         char const *format, va_list args) G_GNUC_PRINTF (2, 0);
133
134 static gsf_off_t
135 gsf_output_memory_vprintf (GsfOutput *output, char const *format, va_list args)
136 {
137         GsfOutputMemory *mem = (GsfOutputMemory *)output;
138
139         if (mem->buffer) {
140                 va_list args2;
141                 gsf_off_t len;
142
143                 /*
144                  * We need to make a copy as args will become unusable after
145                  * the g_vsnprintf call.
146                  */
147                 G_VA_COPY (args2, args);
148
149                 len = g_vsnprintf (mem->buffer + output->cur_offset,
150                                    mem->capacity - output->cur_offset,
151                                    format, args);
152
153                 /* There was insufficient space */
154                 if (len >= (gsf_off_t)(mem->capacity - output->cur_offset))
155                         len = parent_class->Vprintf (output, format, args2);
156
157                 va_end (args2);
158
159                 return len;
160         }
161         return parent_class->Vprintf (output, format, args);
162 }
163
164 static void
165 gsf_output_memory_init (GObject *obj)
166 {
167         GsfOutputMemory *mem = GSF_OUTPUT_MEMORY (obj);
168
169         mem->buffer   = NULL;
170         mem->capacity = 0;
171 }
172
173 static void
174 gsf_output_memory_class_init (GObjectClass *gobject_class)
175 {
176         GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
177         
178         gobject_class->finalize = gsf_output_memory_finalize;
179         output_class->Close     = gsf_output_memory_close;
180         output_class->Seek      = gsf_output_memory_seek;
181         output_class->Write     = gsf_output_memory_write;
182         output_class->Vprintf   = gsf_output_memory_vprintf;
183
184         parent_class = GSF_OUTPUT_CLASS (g_type_class_peek_parent (gobject_class));
185 }
186
187 /**
188  * gsf_output_memory_get_bytes :
189  * @mem : the output device.
190  * 
191  * Returns: The data that has been written to @mem, or %null
192  **/
193 const guint8 *
194 gsf_output_memory_get_bytes (GsfOutputMemory * mem)
195 {
196         g_return_val_if_fail (mem != NULL, NULL);
197         return mem->buffer;
198 }
199
200 GSF_CLASS (GsfOutputMemory, gsf_output_memory,
201            gsf_output_memory_class_init, gsf_output_memory_init,
202            GSF_OUTPUT_TYPE)
203