From 6d071b4ab6d60aecf9532d1aac2128dd0c08f917 Mon Sep 17 00:00:00 2001 From: Juerg Billeter Date: Tue, 27 Nov 2007 13:44:48 +0000 Subject: [PATCH] New functions for efficient access to buffer and simple single byte reads. 2007-11-27 Juerg Billeter * gbufferedinputstream.c: (g_buffered_input_stream_peek_buffer), (g_buffered_input_stream_read_byte): * gbufferedinputstream.h: New functions for efficient access to buffer and simple single byte reads. * gdatainputstream.c: (scan_for_newline), (scan_for_chars), (g_data_input_stream_read_until): * gdatainputstream.h: Use peek_buffer to avoid memcpy in scan_for_newline, implement read_until with multiple stop chars. svn path=/trunk/; revision=5952 --- gio/ChangeLog | 14 +++ gio/gbufferedinputstream.c | 104 +++++++++++++++++++++ gio/gbufferedinputstream.h | 6 ++ gio/gdatainputstream.c | 218 ++++++++++++++++++++++++++++++++------------- gio/gdatainputstream.h | 2 +- 5 files changed, 279 insertions(+), 65 deletions(-) diff --git a/gio/ChangeLog b/gio/ChangeLog index 07725e2..cdd4a76 100644 --- a/gio/ChangeLog +++ b/gio/ChangeLog @@ -1,3 +1,17 @@ +2007-11-27 Jürg Billeter + + * gbufferedinputstream.c: (g_buffered_input_stream_peek_buffer), + (g_buffered_input_stream_read_byte): + * gbufferedinputstream.h: + New functions for efficient access to buffer and simple single byte + reads. + + * gdatainputstream.c: (scan_for_newline), (scan_for_chars), + (g_data_input_stream_read_until): + * gdatainputstream.h: + Use peek_buffer to avoid memcpy in scan_for_newline, implement + read_until with multiple stop chars. + 2007-11-27 Alexander Larsson * Makefile.am: diff --git a/gio/gbufferedinputstream.c b/gio/gbufferedinputstream.c index 18469a0..dfd6c2e 100644 --- a/gio/gbufferedinputstream.c +++ b/gio/gbufferedinputstream.c @@ -1,6 +1,7 @@ /* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2007 Jürg Billeter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -577,6 +578,34 @@ g_buffered_input_stream_peek (GBufferedInputStream *stream, return count; } +/** + * g_buffered_input_stream_peek_buffer: + * @stream: a #GBufferedInputStream. + * @count: a #gsize to get the number of bytes available in the buffer. + * + * Returns the buffer with the currently available bytes. The returned + * buffer must not be modified and will become invalid when reading from + * the stream or filling the buffer. + * + * Returns: read-only buffer + **/ +const void* +g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream, + gsize *count) +{ + GBufferedInputStreamPrivate *priv; + + g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), NULL); + + priv = stream->priv; + + if (count) { + *count = priv->end - priv->pos; + } + + return priv->buffer + priv->pos; +} + static void compact_buffer (GBufferedInputStream *stream) { @@ -785,6 +814,81 @@ g_buffered_input_stream_read (GInputStream *stream, return bytes_read; } +/** + * g_buffered_input_stream_read_byte: + * @stream: #GBufferedInputStream. + * @cancellable: optional #GCancellable object, %NULL to ignore. + * @error: location to store the error occuring, or %NULL to ignore. + * + * Tries to read a single byte from the stream or the buffer. Will block + * during this read. + * + * On success, the byte read from the stream is returned. On end of stream + * -1 is returned but it's not an exceptional error and @error is not set. + * + * If @cancellable is not %NULL, then the operation can be cancelled by + * triggering the cancellable object from another thread. If the operation + * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an + * operation was partially finished when the operation was cancelled the + * partial result will be returned, without an error. + * + * On error -1 is returned and @error is set accordingly. + * + * Returns: the byte read from the @stream, or -1 on end of stream or error. + **/ +int +g_buffered_input_stream_read_byte (GBufferedInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GBufferedInputStreamPrivate *priv; + GInputStream *input_stream; + gsize available; + gssize nread; + + g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1); + + priv = stream->priv; + input_stream = G_INPUT_STREAM (stream); + + if (g_input_stream_is_closed (input_stream)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, + _("Stream is already closed")); + return -1; + } + + if (g_input_stream_has_pending (input_stream)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING, + _("Stream has outstanding operation")); + return -1; + } + + available = priv->end - priv->pos; + + if (available < 1) + return priv->buffer[priv->pos++]; + + /* Byte not available, request refill for more */ + + if (cancellable) + g_push_current_cancellable (cancellable); + + priv->pos = 0; + priv->end = 0; + + nread = g_buffered_input_stream_fill (stream, priv->len, cancellable, error); + + if (cancellable) + g_pop_current_cancellable (cancellable); + + if (nread <= 0) + return -1; /* error or end of stream */ + + return priv->buffer[priv->pos++]; +} + /* ************************** */ /* Async stuff implementation */ /* ************************** */ diff --git a/gio/gbufferedinputstream.h b/gio/gbufferedinputstream.h index eab35b5..fcf9cb4 100644 --- a/gio/gbufferedinputstream.h +++ b/gio/gbufferedinputstream.h @@ -89,6 +89,8 @@ gsize g_buffered_input_stream_peek (GBufferedInputStream *st void *buffer, gsize offset, gsize count); +const void* g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream, + gsize *count); gssize g_buffered_input_stream_fill (GBufferedInputStream *stream, gssize count, @@ -104,6 +106,10 @@ gssize g_buffered_input_stream_fill_finish (GBufferedInputStream *st GAsyncResult *result, GError **error); +int g_buffered_input_stream_read_byte (GBufferedInputStream *stream, + GCancellable *cancellable, + GError **error); + G_END_DECLS diff --git a/gio/gdatainputstream.c b/gio/gdatainputstream.c index 54a4678..a55e531 100644 --- a/gio/gdatainputstream.c +++ b/gio/gdatainputstream.c @@ -1,6 +1,7 @@ /* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2007 Jürg Billeter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -523,7 +524,7 @@ scan_for_newline (GDataInputStream *stream, { GBufferedInputStream *bstream; GDataInputStreamPrivate *priv; - char buffer[100]; + const char *buffer; gsize start, end, peeked; int i; gssize found_pos; @@ -534,84 +535,80 @@ scan_for_newline (GDataInputStream *stream, priv = stream->priv; bstream = G_BUFFERED_INPUT_STREAM (stream); - - available = g_buffered_input_stream_get_available (bstream); checked = *checked_out; last_saw_cr = *last_saw_cr_out; found_pos = -1; newline_len = 0; - while (checked < available) - { - start = checked; - end = MIN (start + sizeof(buffer), available); - peeked = g_buffered_input_stream_peek (bstream, buffer, start, end - start); - end = start + peeked; + start = checked; + buffer = g_buffered_input_stream_peek_buffer (bstream, &available) + start; + end = available; + peeked = end - start; - for (i = 0; i < peeked; i++) + for (i = 0; checked < available && i < peeked; i++) + { + switch (priv->newline_type) { - switch (priv->newline_type) + case G_DATA_STREAM_NEWLINE_TYPE_LF: + if (buffer[i] == 10) { - case G_DATA_STREAM_NEWLINE_TYPE_LF: - if (buffer[i] == 10) - { - found_pos = start + i; - newline_len = 1; - } - break; - case G_DATA_STREAM_NEWLINE_TYPE_CR: - if (buffer[i] == 13) - { - found_pos = start + i; - newline_len = 1; - } - break; - case G_DATA_STREAM_NEWLINE_TYPE_CR_LF: - if (last_saw_cr && buffer[i] == 10) + found_pos = start + i; + newline_len = 1; + } + break; + case G_DATA_STREAM_NEWLINE_TYPE_CR: + if (buffer[i] == 13) + { + found_pos = start + i; + newline_len = 1; + } + break; + case G_DATA_STREAM_NEWLINE_TYPE_CR_LF: + if (last_saw_cr && buffer[i] == 10) + { + found_pos = start + i - 1; + newline_len = 2; + } + break; + default: + case G_DATA_STREAM_NEWLINE_TYPE_ANY: + if (buffer[i] == 10) /* LF */ + { + if (last_saw_cr) { + /* CR LF */ found_pos = start + i - 1; newline_len = 2; } - break; - default: - case G_DATA_STREAM_NEWLINE_TYPE_ANY: - if (buffer[i] == 10) /* LF */ - { - if (last_saw_cr) - { - /* CR LF */ - found_pos = start + i - 1; - newline_len = 2; - } - else - { - /* LF */ - found_pos = start + i; - newline_len = 1; - } - } - else if (last_saw_cr) + else { - /* Last was cr, this is not LF, end is CR */ - found_pos = start + i - 1; + /* LF */ + found_pos = start + i; newline_len = 1; } - /* Don't check for CR here, instead look at last_saw_cr on next byte */ - break; } - - last_saw_cr = (buffer[i] == 13); - - if (found_pos != -1) + else if (last_saw_cr) { - *newline_len_out = newline_len; - return found_pos; + /* Last was cr, this is not LF, end is CR */ + found_pos = start + i - 1; + newline_len = 1; } + /* Don't check for CR here, instead look at last_saw_cr on next byte */ + break; + } + + last_saw_cr = (buffer[i] == 13); + + if (found_pos != -1) + { + *newline_len_out = newline_len; + return found_pos; } - checked = end; } + checked = end; + *checked_out = checked; *last_saw_cr_out = last_saw_cr; return -1; @@ -694,25 +691,118 @@ g_data_input_stream_read_line (GDataInputStream *stream, } +static gssize +scan_for_chars (GDataInputStream *stream, + gsize *checked_out, + const char *stop_chars) +{ + GBufferedInputStream *bstream; + GDataInputStreamPrivate *priv; + const char *buffer; + gsize start, end, peeked; + int i; + gssize found_pos; + gsize available, checked; + const char *stop_char; + + priv = stream->priv; + + bstream = G_BUFFERED_INPUT_STREAM (stream); + + checked = *checked_out; + found_pos = -1; + + start = checked; + buffer = g_buffered_input_stream_peek_buffer (bstream, &available) + start; + end = available; + peeked = end - start; + + for (i = 0; checked < available && i < peeked; i++) + { + for (stop_char = stop_chars; *stop_char != '\0'; stop_char++) + { + if (buffer[i] == *stop_char) + return (start + i); + } + } + + checked = end; + + *checked_out = checked; + return -1; +} + /** * g_data_input_stream_read_until: * @stream: a given #GDataInputStream. - * @stop_char: character to terminate the read. + * @stop_chars: characters to terminate the read. * @length: a #gsize to get the length of the data read in. * @cancellable: optional #GCancellable object, %NULL to ignore. * @error: #GError for error reporting. * - * NOTE: not supported for #GDataInputStream. - * Returns %NULL. + * Returns a string with the data that was read before encountering any of + * the stop characters. Set @length to a #gsize to get the length of the + * read line. This function will return %NULL on an error. **/ char * g_data_input_stream_read_until (GDataInputStream *stream, - gchar stop_char, + const gchar *stop_chars, gsize *length, GCancellable *cancellable, GError **error) { - /* TODO: should be implemented */ - g_assert_not_reached (); - return NULL; + GBufferedInputStream *bstream; + gsize checked; + gssize found_pos; + gssize res; + int stop_char_len; + char *data_until; + + g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL); + + bstream = G_BUFFERED_INPUT_STREAM (stream); + + stop_char_len = 1; + checked = 0; + + while ((found_pos = scan_for_chars (stream, &checked, stop_chars)) == -1) + { + if (g_buffered_input_stream_get_available (bstream) == + g_buffered_input_stream_get_buffer_size (bstream)) + g_buffered_input_stream_set_buffer_size (bstream, + 2 * g_buffered_input_stream_get_buffer_size (bstream)); + + res = g_buffered_input_stream_fill (bstream, -1, cancellable, error); + if (res < 0) + return NULL; + if (res == 0) + { + /* End of stream */ + if (g_buffered_input_stream_get_available (bstream) == 0) + { + if (length) + *length = 0; + return NULL; + } + else + { + found_pos = checked; + stop_char_len = 0; + break; + } + } + } + + data_until = g_malloc (found_pos + stop_char_len + 1); + + res = g_input_stream_read (G_INPUT_STREAM (stream), + data_until, + found_pos + stop_char_len, + NULL, NULL); + if (length) + *length = (gsize)found_pos; + g_assert (res == found_pos + stop_char_len); + data_until[found_pos] = 0; + + return data_until; } diff --git a/gio/gdatainputstream.h b/gio/gdatainputstream.h index af58dbf..d68be6c 100644 --- a/gio/gdatainputstream.h +++ b/gio/gdatainputstream.h @@ -107,7 +107,7 @@ char * g_data_input_stream_read_line (GDataInputStream GCancellable *cancellable, GError **error); char * g_data_input_stream_read_until (GDataInputStream *stream, - gchar stop_char, + const gchar *stop_chars, gsize *length, GCancellable *cancellable, GError **error); -- 2.7.4