1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
3 /* camel-stream-buffer.c : Buffer any other other stream
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU Lesser General Public
11 * License as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
32 #include <sys/types.h>
34 #include "camel-stream-buffer.h"
36 #define CAMEL_STREAM_BUFFER_GET_PRIVATE(obj) \
37 (G_TYPE_INSTANCE_GET_PRIVATE \
38 ((obj), CAMEL_TYPE_STREAM_BUFFER, CamelStreamBufferPrivate))
40 struct _CamelStreamBufferPrivate {
44 guchar *buf, *ptr, *end;
47 guchar *linebuf; /* for reading lines at a time */
50 CamelStreamBufferMode mode;
54 G_DEFINE_TYPE (CamelStreamBuffer, camel_stream_buffer, CAMEL_TYPE_STREAM)
57 BUF_USER = 1 << 0 /* user-supplied buffer, do not free */
62 /* only returns the number passed in, or -1 on an error */
64 stream_write_all (CamelStream *stream,
67 GCancellable *cancellable,
73 w = camel_stream_write (
74 stream, buffer, left, cancellable, error);
85 set_vbuf (CamelStreamBuffer *stream,
87 CamelStreamBufferMode mode,
90 CamelStreamBufferPrivate *priv;
92 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
94 if (priv->buf && !(priv->flags & BUF_USER))
98 priv->buf = (guchar *) buf;
99 priv->flags |= BUF_USER;
101 priv->buf = g_malloc (size);
102 priv->flags &= ~BUF_USER;
105 priv->ptr = priv->buf;
106 priv->end = priv->buf;
112 stream_buffer_dispose (GObject *object)
114 CamelStreamBufferPrivate *priv;
116 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (object);
118 if (priv->stream != NULL) {
119 g_object_unref (priv->stream);
123 /* Chain up to parent's dispose() method. */
124 G_OBJECT_CLASS (camel_stream_buffer_parent_class)->dispose (object);
128 stream_buffer_finalize (GObject *object)
130 CamelStreamBufferPrivate *priv;
132 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (object);
134 if (!(priv->flags & BUF_USER))
137 g_free (priv->linebuf);
139 /* Chain up to parent's finalize() method. */
140 G_OBJECT_CLASS (camel_stream_buffer_parent_class)->finalize (object);
144 stream_buffer_read (CamelStream *stream,
147 GCancellable *cancellable,
150 CamelStreamBufferPrivate *priv;
151 gssize bytes_read = 1;
153 gchar *bptr = buffer;
154 GError *local_error = NULL;
156 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
158 g_return_val_if_fail (
159 (priv->mode & CAMEL_STREAM_BUFFER_MODE) ==
160 CAMEL_STREAM_BUFFER_READ, 0);
162 while (n && bytes_read > 0) {
163 bytes_left = priv->end - priv->ptr;
164 if (bytes_left < n) {
165 if (bytes_left > 0) {
166 memcpy (bptr, priv->ptr, bytes_left);
169 priv->ptr += bytes_left;
171 /* if we are reading a lot, then read directly to the destination buffer */
172 if (n >= priv->size / 3) {
173 bytes_read = camel_stream_read (
174 priv->stream, bptr, n,
175 cancellable, &local_error);
176 if (bytes_read > 0) {
181 bytes_read = camel_stream_read (
182 priv->stream, (gchar *) priv->buf,
183 priv->size, cancellable, &local_error);
184 if (bytes_read > 0) {
185 gsize bytes_used = bytes_read > n ? n : bytes_read;
186 priv->ptr = priv->buf;
187 priv->end = priv->buf + bytes_read;
188 memcpy (bptr, priv->ptr, bytes_used);
189 priv->ptr += bytes_used;
195 memcpy (bptr, priv->ptr, n);
202 /* If camel_stream_read() failed but we managed to read some data
203 * before the failure, discard the error and return the number of
204 * bytes read. If we didn't read any data, propagate the error. */
205 if (local_error != NULL) {
207 g_clear_error (&local_error);
209 g_propagate_error (error, local_error);
214 return (gssize)(bptr - buffer);
218 stream_buffer_write (CamelStream *stream,
221 GCancellable *cancellable,
224 CamelStreamBufferPrivate *priv;
228 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
230 g_return_val_if_fail (
231 (priv->mode & CAMEL_STREAM_BUFFER_MODE) ==
232 CAMEL_STREAM_BUFFER_WRITE, 0);
234 /* first, copy as much as we can */
235 left = priv->size - (priv->ptr - priv->buf);
236 todo = MIN (left, n);
238 memcpy (priv->ptr, buffer, todo);
243 /* if we've filled the buffer, write it out, reset buffer */
245 if (stream_write_all (
246 priv->stream, (gchar *) priv->buf,
247 priv->size, cancellable, error) == -1)
250 priv->ptr = priv->buf;
253 /* if we still have more, write directly, or copy to buffer */
255 if (n >= priv->size / 3) {
256 if (stream_write_all (
257 priv->stream, buffer, n,
258 cancellable, error) == -1)
261 memcpy (priv->ptr, buffer, n);
270 stream_buffer_flush (CamelStream *stream,
271 GCancellable *cancellable,
274 CamelStreamBufferPrivate *priv;
276 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
278 if ((priv->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE) {
279 gsize len = priv->ptr - priv->buf;
281 if (camel_stream_write (
282 priv->stream, (gchar *) priv->buf,
283 len, cancellable, error) == -1)
286 priv->ptr = priv->buf;
288 /* nothing to do for read mode 'flush' */
291 return camel_stream_flush (priv->stream, cancellable, error);
295 stream_buffer_close (CamelStream *stream,
296 GCancellable *cancellable,
299 CamelStreamBufferPrivate *priv;
301 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
303 if (stream_buffer_flush (stream, cancellable, error) == -1)
306 return camel_stream_close (priv->stream, cancellable, error);
310 stream_buffer_eos (CamelStream *stream)
312 CamelStreamBufferPrivate *priv;
314 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
316 return camel_stream_eos (priv->stream) && priv->ptr == priv->end;
320 stream_buffer_init_vbuf (CamelStreamBuffer *stream,
321 CamelStream *other_stream,
322 CamelStreamBufferMode mode,
326 CamelStreamBufferPrivate *priv;
328 priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
330 set_vbuf (stream, buf, mode, size);
332 if (priv->stream != NULL)
333 g_object_unref (priv->stream);
335 priv->stream = g_object_ref (other_stream);
339 stream_buffer_init_method (CamelStreamBuffer *stream,
340 CamelStream *other_stream,
341 CamelStreamBufferMode mode)
343 stream_buffer_init_vbuf (stream, other_stream, mode, NULL, BUF_SIZE);
347 camel_stream_buffer_class_init (CamelStreamBufferClass *class)
349 GObjectClass *object_class;
350 CamelStreamClass *stream_class;
352 g_type_class_add_private (class, sizeof (CamelStreamBufferPrivate));
354 object_class = G_OBJECT_CLASS (class);
355 object_class->dispose = stream_buffer_dispose;
356 object_class->finalize = stream_buffer_finalize;
358 stream_class = CAMEL_STREAM_CLASS (class);
359 stream_class->read = stream_buffer_read;
360 stream_class->write = stream_buffer_write;
361 stream_class->flush = stream_buffer_flush;
362 stream_class->close = stream_buffer_close;
363 stream_class->eos = stream_buffer_eos;
365 class->init = stream_buffer_init_method;
366 class->init_vbuf = stream_buffer_init_vbuf;
370 camel_stream_buffer_init (CamelStreamBuffer *stream)
372 stream->priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
373 stream->priv->flags = 0;
374 stream->priv->size = BUF_SIZE;
375 stream->priv->buf = g_malloc (BUF_SIZE);
376 stream->priv->ptr = stream->priv->buf;
377 stream->priv->end = stream->priv->buf;
379 CAMEL_STREAM_BUFFER_READ |
380 CAMEL_STREAM_BUFFER_BUFFER;
381 stream->priv->stream = NULL;
382 stream->priv->linesize = 80;
383 stream->priv->linebuf = g_malloc (stream->priv->linesize);
387 * camel_stream_buffer_new:
388 * @stream: a #CamelStream object to buffer
389 * @mode: Operational mode of buffered stream.
391 * Create a new buffered stream of another stream. A default
392 * buffer size (1024 bytes), automatically managed will be used
395 * See camel_stream_buffer_new_with_vbuf() for details on the
398 * Returns: a newly created buffered stream.
401 camel_stream_buffer_new (CamelStream *stream,
402 CamelStreamBufferMode mode)
404 CamelStreamBuffer *sbf;
405 CamelStreamBufferClass *class;
407 g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
409 sbf = g_object_new (CAMEL_TYPE_STREAM_BUFFER, NULL);
411 class = CAMEL_STREAM_BUFFER_GET_CLASS (sbf);
412 g_return_val_if_fail (class->init != NULL, NULL);
414 class->init (sbf, stream, mode);
416 return CAMEL_STREAM (sbf);
420 * camel_stream_buffer_new_with_vbuf:
421 * @stream: An existing stream to buffer.
422 * @mode: Mode to buffer in.
423 * @buf: Memory to use for buffering.
424 * @size: Size of buffer to use.
426 * Create a new stream which buffers another stream, @stream.
428 * The following values are available for @mode:
430 * #CAMEL_STREAM_BUFFER_BUFFER, Buffer the input/output in blocks.
431 * #CAMEL_STREAM_BUFFER_NEWLINE, Buffer on newlines (for output).
432 * #CAMEL_STREAM_BUFFER_NONE, Perform no buffering.
434 * Note that currently this is ignored and #CAMEL_STREAM_BUFFER_BUFFER
437 * In addition, one of the following mode options should be or'd
438 * together with the buffering mode:
440 * #CAMEL_STREAM_BUFFER_WRITE, Buffer in write mode.
441 * #CAMEL_STREAM_BUFFER_READ, Buffer in read mode.
443 * Buffering can only be done in one direction for any
446 * If @buf is non-NULL, then use the memory pointed to
447 * (for upto @size bytes) as the buffer for all buffering
448 * operations. It is upto the application to free this buffer.
449 * If @buf is NULL, then allocate and manage @size bytes
452 * Returns: A new stream with buffering applied.
455 camel_stream_buffer_new_with_vbuf (CamelStream *stream,
456 CamelStreamBufferMode mode,
460 CamelStreamBuffer *sbf;
461 CamelStreamBufferClass *class;
463 g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
464 g_return_val_if_fail (buf != NULL, NULL);
466 sbf = g_object_new (CAMEL_TYPE_STREAM_BUFFER, NULL);
468 class = CAMEL_STREAM_BUFFER_GET_CLASS (sbf);
469 g_return_val_if_fail (class->init_vbuf != NULL, NULL);
471 class->init_vbuf (sbf, stream, mode, buf, size);
473 return CAMEL_STREAM (sbf);
477 * camel_stream_buffer_gets:
478 * @sbf: a #CamelStreamBuffer object
479 * @buf: Memory to write the string to.
480 * @max: Maxmimum number of characters to store.
481 * @cancellable: optional #GCancellable object, or %NULL
482 * @error: return location for a #GError, or %NULL
484 * Read a line of characters up to the next newline character or
487 * If the newline character is encountered, then it will be
488 * included in the buffer @buf. The buffer will be %NULL terminated.
490 * Returns: the number of characters read, or %0 for end of file,
494 camel_stream_buffer_gets (CamelStreamBuffer *sbf,
497 GCancellable *cancellable,
500 register gchar *outptr, *inptr, *inend, c, *outend;
502 GError *local_error = NULL;
505 inptr = (gchar *) sbf->priv->ptr;
506 inend = (gchar *) sbf->priv->end;
507 outend = buf + max-1; /* room for NUL */
510 while (inptr < inend && outptr < outend) {
515 sbf->priv->ptr = (guchar *) inptr;
519 if (outptr == outend)
522 bytes_read = camel_stream_read (
523 sbf->priv->stream, (gchar *) sbf->priv->buf,
524 sbf->priv->size, cancellable, &local_error);
525 if (bytes_read == -1) {
528 g_propagate_error (error, local_error);
533 sbf->priv->ptr = sbf->priv->buf;
534 sbf->priv->end = sbf->priv->buf + bytes_read;
535 inptr = (gchar *) sbf->priv->ptr;
536 inend = (gchar *) sbf->priv->end;
537 } while (bytes_read > 0);
539 sbf->priv->ptr = (guchar *) inptr;
542 g_clear_error (&local_error);
544 return (gint)(outptr - buf);
548 * camel_stream_buffer_read_line:
549 * @sbf: a #CamelStreamBuffer object
550 * @cancellable: optional #GCancellable object, or %NULL
551 * @error: return location for a @GError, or %NULL
553 * This function reads a complete newline-terminated line from the stream
554 * and returns it in allocated memory. The trailing newline (and carriage
555 * return if any) are not included in the returned string.
557 * Returns: the line read, which the caller must free when done with,
558 * or %NULL on eof. If an error occurs, @error will be set.
561 camel_stream_buffer_read_line (CamelStreamBuffer *sbf,
562 GCancellable *cancellable,
567 GError *local_error = NULL;
569 p = sbf->priv->linebuf;
572 nread = camel_stream_buffer_gets (
573 sbf, (gchar *) p, sbf->priv->linesize -
574 (p - sbf->priv->linebuf), cancellable, &local_error);
576 if (p > sbf->priv->linebuf)
579 g_propagate_error (error, local_error);
587 nread = p - sbf->priv->linebuf;
588 sbf->priv->linesize *= 2;
589 sbf->priv->linebuf = g_realloc (
590 sbf->priv->linebuf, sbf->priv->linesize);
591 p = sbf->priv->linebuf + nread;
595 if (p > sbf->priv->linebuf && p[-1] == '\r')
599 g_clear_error (&local_error);
601 return g_strdup ((gchar *) sbf->priv->linebuf);