New functions for efficient access to buffer and simple single byte reads.
authorJuerg Billeter <j@bitron.ch>
Tue, 27 Nov 2007 13:44:48 +0000 (13:44 +0000)
committerJürg Billeter <juergbi@src.gnome.org>
Tue, 27 Nov 2007 13:44:48 +0000 (13:44 +0000)
2007-11-27  Juerg Billeter  <j@bitron.ch>

* 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
gio/gbufferedinputstream.c
gio/gbufferedinputstream.h
gio/gdatainputstream.c
gio/gdatainputstream.h

index 07725e2..cdd4a76 100644 (file)
@@ -1,3 +1,17 @@
+2007-11-27  Jürg Billeter  <j@bitron.ch>
+
+       * 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  <alexl@redhat.com>
 
         * Makefile.am:
index 18469a0..dfd6c2e 100644 (file)
@@ -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 */
 /* ************************** */
index eab35b5..fcf9cb4 100644 (file)
@@ -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
 
index 54a4678..a55e531 100644 (file)
@@ -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;
 }
index af58dbf..d68be6c 100644 (file)
@@ -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);