1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-stream-mem.c: memory buffer based stream */
5 * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
6 * Michael Zucchi <notzed@ximian.com>
8 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU Lesser General Public
12 * License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
33 #include <sys/types.h>
35 #include "camel-stream-mem.h"
37 #define CAMEL_STREAM_MEM_GET_PRIVATE(obj) \
38 (G_TYPE_INSTANCE_GET_PRIVATE \
39 ((obj), CAMEL_TYPE_STREAM_MEM, CamelStreamMemPrivate))
41 struct _CamelStreamMemPrivate {
42 guint owner : 1; /* do we own the buffer? */
43 guint secure : 1; /* do we clear the buffer on finalize?
44 (only if we own it) */
50 /* Forward Declarations */
51 static void camel_stream_mem_seekable_init (GSeekableIface *interface);
53 G_DEFINE_TYPE_WITH_CODE (
54 CamelStreamMem, camel_stream_mem, CAMEL_TYPE_STREAM,
55 G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, camel_stream_mem_seekable_init))
57 /* could probably be a util method */
59 clear_mem (gpointer p,
64 /* This also helps debug bad access memory errors */
73 memset (s, 0xbf, len);
77 stream_mem_finalize (GObject *object)
79 CamelStreamMemPrivate *priv;
81 priv = CAMEL_STREAM_MEM_GET_PRIVATE (object);
83 if (priv->buffer && priv->owner) {
84 /* TODO: we need our own bytearray type since we don't know
85 * the real size of the underlying buffer :-/ */
86 if (priv->secure && priv->buffer->len)
87 clear_mem (priv->buffer->data, priv->buffer->len);
88 g_byte_array_free (priv->buffer, TRUE);
91 /* Chain up to parent's finalize() method. */
92 G_OBJECT_CLASS (camel_stream_mem_parent_class)->finalize (object);
96 stream_mem_read (CamelStream *stream,
99 GCancellable *cancellable,
102 CamelStreamMemPrivate *priv;
105 priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
107 nread = MIN (n, priv->buffer->len - priv->position);
109 memcpy (buffer, priv->buffer->data + priv->position, nread);
110 priv->position += nread;
118 stream_mem_write (CamelStream *stream,
121 GCancellable *cancellable,
124 CamelStreamMemPrivate *priv;
127 priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
129 /* FIXME: we shouldn't use g_byte_arrays or g_malloc perhaps? */
130 if (priv->position == priv->buffer->len) {
131 g_byte_array_append (priv->buffer, (const guint8 *) buffer, nwrite);
133 g_byte_array_set_size (priv->buffer, nwrite + priv->buffer->len);
134 memcpy (priv->buffer->data + priv->position, buffer, nwrite);
136 priv->position += nwrite;
142 stream_mem_eos (CamelStream *stream)
144 CamelStreamMemPrivate *priv;
146 priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
148 return priv->buffer->len <= priv->position;
152 stream_mem_tell (GSeekable *seekable)
154 CamelStreamMemPrivate *priv;
156 priv = CAMEL_STREAM_MEM_GET_PRIVATE (seekable);
158 return priv->position;
162 stream_mem_can_seek (GSeekable *seekable)
168 stream_mem_seek (GSeekable *seekable,
171 GCancellable *cancellable,
174 CamelStreamMemPrivate *priv;
177 priv = CAMEL_STREAM_MEM_GET_PRIVATE (seekable);
184 position = priv->position + offset;
187 position = (priv->buffer)->len + offset;
194 position = MAX (position, 0);
196 if (position > priv->buffer->len) {
197 gint oldlen = priv->buffer->len;
198 g_byte_array_set_size (priv->buffer, position);
199 memset (priv->buffer->data + oldlen, 0, position - oldlen);
202 priv->position = position;
208 stream_mem_can_truncate (GSeekable *seekable)
214 stream_mem_truncate_fn (GSeekable *seekable,
216 GCancellable *cancellable,
219 /* XXX Don't bother translating this. Camel never calls it. */
221 error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
222 "Truncation is not supported");
228 camel_stream_mem_class_init (CamelStreamMemClass *class)
230 GObjectClass *object_class;
231 CamelStreamClass *stream_class;
233 g_type_class_add_private (class, sizeof (CamelStreamMemPrivate));
235 object_class = G_OBJECT_CLASS (class);
236 object_class->finalize = stream_mem_finalize;
238 stream_class = CAMEL_STREAM_CLASS (class);
239 stream_class->read = stream_mem_read;
240 stream_class->write = stream_mem_write;
241 stream_class->eos = stream_mem_eos;
245 camel_stream_mem_seekable_init (GSeekableIface *interface)
247 interface->tell = stream_mem_tell;
248 interface->can_seek = stream_mem_can_seek;
249 interface->seek = stream_mem_seek;
250 interface->can_truncate = stream_mem_can_truncate;
251 interface->truncate_fn = stream_mem_truncate_fn;
255 camel_stream_mem_init (CamelStreamMem *stream)
257 stream->priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
261 * camel_stream_mem_new:
263 * Create a new #CamelStreamMem object.
265 * Returns: a new #CamelStreamMem
268 camel_stream_mem_new (void)
270 return camel_stream_mem_new_with_byte_array (g_byte_array_new ());
274 * camel_stream_mem_new_with_buffer:
275 * @buffer: a memory buffer to use as the stream data
276 * @len: length of @buffer
278 * Create a new memory stream using @buffer as the stream data.
280 * Note: @buffer will be copied into an internal #GByteArray structure
281 * for use as the stream backing. This may have resource implications
282 * you may wish to consider.
284 * Returns: a new #CamelStreamMem
287 camel_stream_mem_new_with_buffer (const gchar *buffer,
292 g_return_val_if_fail (buffer != NULL, NULL);
294 ba = g_byte_array_new ();
295 g_byte_array_append (ba, (const guint8 *) buffer, len);
297 return camel_stream_mem_new_with_byte_array (ba);
301 * camel_stream_mem_new_with_byte_array:
302 * @buffer: a #GByteArray to use as the stream data
304 * Create a new #CamelStreamMem using @buffer as the stream data.
306 * Note: The newly created #CamelStreamMem will destroy @buffer
309 * Returns: a new #CamelStreamMem
312 camel_stream_mem_new_with_byte_array (GByteArray *buffer)
315 CamelStreamMemPrivate *priv;
317 g_return_val_if_fail (buffer != NULL, NULL);
319 stream = g_object_new (CAMEL_TYPE_STREAM_MEM, NULL);
320 priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
322 priv->buffer = buffer;
329 * camel_stream_mem_set_secure:
330 * @mem: a #CamelStreamMem object
332 * Mark the memory stream as secure. At the very least this means the
333 * data in the buffer will be cleared when the buffer is finalized.
334 * This only applies to buffers owned by the stream.
337 camel_stream_mem_set_secure (CamelStreamMem *mem)
339 g_return_if_fail (CAMEL_IS_STREAM_MEM (mem));
341 mem->priv->secure = 1;
344 /* note: with these functions the caller is the 'owner' of the buffer */
347 * camel_stream_mem_get_byte_array:
348 * @mem: a #CamelStreamMem
353 camel_stream_mem_get_byte_array (CamelStreamMem *mem)
355 g_return_val_if_fail (CAMEL_IS_STREAM_MEM (mem), NULL);
357 return mem->priv->buffer;
361 * camel_stream_mem_set_byte_array:
362 * @mem: a #CamelStreamMem object
363 * @buffer: a #GByteArray
365 * Set @buffer to be the backing data to the existing #CamelStreamMem, @mem.
367 * Note: @mem will not take ownership of @buffer and so will need to
368 * be freed separately from @mem.
371 camel_stream_mem_set_byte_array (CamelStreamMem *mem,
374 g_return_if_fail (CAMEL_IS_STREAM_MEM (mem));
375 g_return_if_fail (buffer != NULL);
377 if (mem->priv->buffer && mem->priv->owner) {
378 if (mem->priv->secure && mem->priv->buffer->len)
380 mem->priv->buffer->data,
381 mem->priv->buffer->len);
382 g_byte_array_free (mem->priv->buffer, TRUE);
384 mem->priv->owner = FALSE;
385 mem->priv->buffer = buffer;
389 * camel_stream_mem_set_buffer:
390 * @mem: a #CamelStreamMem object
391 * @buffer: a memory buffer
392 * @len: length of @buffer
394 * Set @buffer to be the backing data to the existing #CamelStreamMem, @mem.
396 * Note: @buffer will be copied into an internal #GByteArray structure
397 * and so may have resource implications to consider.
400 camel_stream_mem_set_buffer (CamelStreamMem *mem,
406 g_return_if_fail (CAMEL_IS_STREAM_MEM (mem));
407 g_return_if_fail (buffer != NULL);
409 ba = g_byte_array_new ();
410 g_byte_array_append (ba, (const guint8 *) buffer, len);
411 camel_stream_mem_set_byte_array (mem, ba);
412 mem->priv->owner = TRUE;