[glib] Add g_data_input_stream_read_upto{,async,finish}



commit 2e78d07f86d70de274f126a3ff00bd4af90a5c90
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Mar 23 01:12:01 2010 -0500

    Add g_data_input_stream_read_upto{,async,finish}
    
    These functions are meant to replace the read_until() flavour, with the
    following improvements:
    
      - consistency between the synchronous and asynchronous versions as to
        if the separator character is read (it never is).
    
      - support for using a nul byte as a separator character by way of
        addition of a length parameter which allows stop_chars to be treated
        as a byte array rather than a nul-terminated string.
    
    The read_until() functions are not yet formally deprecated, but a note
    has been added to the documentation warning not to use them as they will
    be in the future.
    
    This is bug #584284.

 docs/reference/gio/gio-sections.txt |    3 +
 gio/gdatainputstream.c              |  257 ++++++++++++++++++++++++++++-------
 gio/gdatainputstream.h              |   66 ++++++----
 gio/gio.symbols                     |    3 +
 gio/tests/data-input-stream.c       |   61 ++++++++-
 5 files changed, 312 insertions(+), 78 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 77011b9..368797c 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -671,6 +671,9 @@ g_data_input_stream_read_uint64
 g_data_input_stream_read_line
 g_data_input_stream_read_line_async
 g_data_input_stream_read_line_finish
+g_data_input_stream_read_upto
+g_data_input_stream_read_upto_async
+g_data_input_stream_read_upto_finish
 g_data_input_stream_read_until
 g_data_input_stream_read_until_async
 g_data_input_stream_read_until_finish
diff --git a/gio/gdatainputstream.c b/gio/gdatainputstream.c
index ad8bf5e..7743d56 100644
--- a/gio/gdatainputstream.c
+++ b/gio/gdatainputstream.c
@@ -30,6 +30,7 @@
 #include "gioerror.h"
 #include "glibintl.h"
 
+#include <string.h>
 
 /**
  * SECTION:gdatainputstream
@@ -811,7 +812,8 @@ g_data_input_stream_read_line (GDataInputStream  *stream,
 static gssize
 scan_for_chars (GDataInputStream *stream,
 		gsize            *checked_out,
-		const char       *stop_chars)
+		const char       *stop_chars,
+                gssize            stop_chars_len)
 {
   GBufferedInputStream *bstream;
   const char *buffer;
@@ -819,8 +821,10 @@ scan_for_chars (GDataInputStream *stream,
   int i;
   gsize available, checked;
   const char *stop_char;
+  const char *stop_end;
 
   bstream = G_BUFFERED_INPUT_STREAM (stream);
+  stop_end = stop_chars + stop_chars_len;
 
   checked = *checked_out;
 
@@ -831,7 +835,7 @@ scan_for_chars (GDataInputStream *stream,
 
   for (i = 0; checked < available && i < peeked; i++)
     {
-      for (stop_char = stop_chars; *stop_char != '\0'; stop_char++)
+      for (stop_char = stop_chars; stop_char != stop_end; stop_char++)
 	{
 	  if (buffer[i] == *stop_char)
 	    return (start + i);
@@ -858,6 +862,12 @@ scan_for_chars (GDataInputStream *stream,
  * Note that, in contrast to g_data_input_stream_read_until_async(),
  * this function consumes the stop character that it finds.
  *
+ * Don't use this function in new code.  Its functionality is
+ * inconsistent with g_data_input_stream_read_until_async().  Both
+ * functions will be marked as deprecated in a future release.  Use
+ * g_data_input_stream_read_upto() instead, but note that that function
+ * does not consume the stop character.
+ *
  * 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 string. This function will return %NULL on an error.
@@ -870,59 +880,24 @@ g_data_input_stream_read_until (GDataInputStream  *stream,
 			       GError            **error)
 {
   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);  
+  gchar *result;
 
   bstream = G_BUFFERED_INPUT_STREAM (stream);
 
-  stop_char_len = 1;
-  checked = 0;
+  result = g_data_input_stream_read_upto (stream, stop_chars, -1,
+                                          length, cancellable, error);
 
-  while ((found_pos = scan_for_chars (stream, &checked, stop_chars)) == -1)
+  /* If we're not at end of stream then we have a stop_char to consume. */
+  if (result != NULL && g_buffered_input_stream_get_available (bstream) > 0)
     {
-      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));
+      gsize res;
+      gchar b;
 
-      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;
-	    }
-	}
+      res = g_input_stream_read (G_INPUT_STREAM (stream), &b, 1, NULL, NULL);
+      g_assert (res == 1);
     }
 
-  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_warn_if_fail (res == found_pos + stop_char_len);
-  data_until[found_pos] = 0;
-  
-  return data_until;
+  return result;
 }
 
 typedef struct
@@ -935,6 +910,7 @@ typedef struct
   GCancellable *cancellable;
 
   gchar *stop_chars;
+  gssize stop_chars_len;
   gchar *line;
   gsize length;
 } GDataInputStreamReadData;
@@ -1010,7 +986,8 @@ g_data_input_stream_read_line_ready (GObject      *object,
     {
       found_pos = scan_for_chars (data->stream,
                                   &data->checked,
-                                  data->stop_chars);
+                                  data->stop_chars,
+                                  data->stop_chars_len);
       newline_len = 0;
     }
   else
@@ -1062,6 +1039,7 @@ g_data_input_stream_read_data_free (gpointer user_data)
 static void
 g_data_input_stream_read_async (GDataInputStream    *stream,
                                 const gchar         *stop_chars,
+                                gssize               stop_chars_len,
                                 gint                 io_priority,
                                 GCancellable        *cancellable,
                                 GAsyncReadyCallback  callback,
@@ -1075,7 +1053,10 @@ g_data_input_stream_read_async (GDataInputStream    *stream,
   if (cancellable)
     g_object_ref (cancellable);
   data->cancellable = cancellable;
-  data->stop_chars = g_strdup (stop_chars);
+  if (stop_chars_len == -1)
+    stop_chars_len = strlen (stop_chars);
+  data->stop_chars = g_memdup (stop_chars, stop_chars_len);
+  data->stop_chars_len = stop_chars_len;
   data->io_priority = io_priority;
   data->last_saw_cr = FALSE;
   data->checked = 0;
@@ -1142,7 +1123,7 @@ g_data_input_stream_read_line_async (GDataInputStream    *stream,
   g_return_if_fail (G_IS_DATA_INPUT_STREAM (stream));
   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
-  g_data_input_stream_read_async (stream, NULL, io_priority,
+  g_data_input_stream_read_async (stream, NULL, 0, io_priority,
                                   cancellable, callback, user_data,
                                   g_data_input_stream_read_line_async);
 }
@@ -1168,6 +1149,11 @@ g_data_input_stream_read_line_async (GDataInputStream    *stream,
  * can then call g_data_input_stream_read_until_finish() to get
  * the result of the operation.
  *
+ * Don't use this function in new code.  Its functionality is
+ * inconsistent with g_data_input_stream_read_until().  Both functions
+ * will be marked as deprecated in a future release.  Use
+ * g_data_input_stream_read_upto_async() instead.
+ *
  * Since: 2.20
  */
 void
@@ -1182,7 +1168,7 @@ g_data_input_stream_read_until_async (GDataInputStream    *stream,
   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
   g_return_if_fail (stop_chars != NULL);
 
-  g_data_input_stream_read_async (stream, stop_chars, io_priority,
+  g_data_input_stream_read_async (stream, stop_chars, -1, io_priority,
                                   cancellable, callback, user_data,
                                   g_data_input_stream_read_until_async);
 }
@@ -1245,3 +1231,172 @@ g_data_input_stream_read_until_finish (GDataInputStream  *stream,
 
   return g_data_input_stream_read_finish (stream, result, length, error);
 }
+
+/**
+ * g_data_input_stream_read_upto:
+ * @stream: a #GDataInputStream
+ * @stop_chars: characters to terminate the read
+ * @stop_chars_len: length of @stop_chars. May be -1 if @stop_chars is
+ *     nul-terminated
+ * @length: a #gsize to get the length of the data read in
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @error: #GError for error reporting
+ *
+ * Reads a string from the data input stream, up to the first
+ * occurrence of any of the stop characters.
+ *
+ * In contrast to g_data_input_stream_read_until(), this function
+ * does <emphasis>not</emphasis> consume the stop character. You have
+ * to use g_data_input_stream_read_byte() to get it before calling
+ * g_data_input_stream_read_upto() again.
+ *
+ * Note that @stop_chars may contain '\0' if @stop_chars_len is
+ * specified.
+ *
+ * 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 string. This function will return %NULL on an error
+ *
+ * Since: 2.24
+ */
+char *
+g_data_input_stream_read_upto (GDataInputStream  *stream,
+                               const gchar       *stop_chars,
+                               gssize             stop_chars_len,
+                               gsize             *length,
+                               GCancellable      *cancellable,
+                               GError           **error)
+{
+  GBufferedInputStream *bstream;
+  gsize checked;
+  gssize found_pos;
+  gssize res;
+  char *data_until;
+
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL);
+
+  if (stop_chars_len < 0)
+    stop_chars_len = strlen (stop_chars);
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+
+  checked = 0;
+
+  while ((found_pos = scan_for_chars (stream, &checked, stop_chars, stop_chars_len)) == -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;
+              break;
+            }
+        }
+    }
+
+  data_until = g_malloc (found_pos + 1);
+
+  res = g_input_stream_read (G_INPUT_STREAM (stream),
+                             data_until,
+                             found_pos,
+                             NULL, NULL);
+  if (length)
+    *length = (gsize)found_pos;
+  g_warn_if_fail (res == found_pos);
+  data_until[found_pos] = 0;
+
+  return data_until;
+}
+
+/**
+ * g_data_input_stream_read_upto_async:
+ * @stream: a #GDataInputStream
+ * @stop_chars: characters to terminate the read
+ * @stop_chars_len: length of @stop_chars. May be -1 if @stop_chars is
+ *     nul-terminated
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * The asynchronous version of g_data_input_stream_read_upto().
+ * It is an error to have two outstanding calls to this function.
+ *
+ * In contrast to g_data_input_stream_read_until(), this function
+ * does <emphasis>not</emphasis> consume the stop character. You have
+ * to use g_data_input_stream_read_byte() to get it before calling
+ * g_data_input_stream_read_upto() again.
+ *
+ * Note that @stop_chars may contain '\0' if @stop_chars_len is
+ * specified.
+ *
+ * When the operation is finished, @callback will be called. You
+ * can then call g_data_input_stream_read_upto_finish() to get
+ * the result of the operation.
+ *
+ * Since: 2.24
+ */
+void
+g_data_input_stream_read_upto_async (GDataInputStream    *stream,
+                                     const gchar         *stop_chars,
+                                     gssize               stop_chars_len,
+                                     gint                 io_priority,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+  g_return_if_fail (G_IS_DATA_INPUT_STREAM (stream));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_return_if_fail (stop_chars != NULL);
+
+  g_data_input_stream_read_async (stream, stop_chars, stop_chars_len, io_priority,
+                                  cancellable, callback, user_data,
+                                  g_data_input_stream_read_upto_async);
+}
+
+/**
+ * g_data_input_stream_read_upto_finish:
+ * @stream: a #GDataInputStream
+ * @result: the #GAsyncResult that was provided to the callback
+ * @length: a #gsize to get the length of the data read in
+ * @error: #GError for error reporting
+ *
+ * Finish an asynchronous call started by
+ * g_data_input_stream_read_upto_async().
+ *
+ * Note that this function does <emphasis>not</emphasis> consume the
+ * stop character. You have to use g_data_input_stream_read_byte() to
+ * get it before calling g_data_input_stream_read_upto_async() again.
+ *
+ * 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 string. This function will return %NULL on an error.
+ *
+ * Since: 2.24
+ */
+gchar *
+g_data_input_stream_read_upto_finish (GDataInputStream  *stream,
+                                      GAsyncResult      *result,
+                                      gsize             *length,
+                                      GError           **error)
+{
+  g_return_val_if_fail (
+    g_simple_async_result_is_valid (result, G_OBJECT (stream),
+      g_data_input_stream_read_upto_async), NULL);
+
+  return g_data_input_stream_read_finish (stream, result, length, error);
+}
diff --git a/gio/gdatainputstream.h b/gio/gdatainputstream.h
index 0e42317..e5724d5 100644
--- a/gio/gdatainputstream.h
+++ b/gio/gdatainputstream.h
@@ -72,36 +72,36 @@ GType                  g_data_input_stream_get_type             (void) G_GNUC_CO
 GDataInputStream *     g_data_input_stream_new                  (GInputStream            *base_stream);
 
 void                   g_data_input_stream_set_byte_order       (GDataInputStream        *stream,
-							         GDataStreamByteOrder     order);
+                                                                 GDataStreamByteOrder     order);
 GDataStreamByteOrder   g_data_input_stream_get_byte_order       (GDataInputStream        *stream);
 void                   g_data_input_stream_set_newline_type     (GDataInputStream        *stream,
-							         GDataStreamNewlineType   type);
+                                                                 GDataStreamNewlineType   type);
 GDataStreamNewlineType g_data_input_stream_get_newline_type     (GDataInputStream        *stream);
 guchar                 g_data_input_stream_read_byte            (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 gint16                 g_data_input_stream_read_int16           (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 guint16                g_data_input_stream_read_uint16          (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 gint32                 g_data_input_stream_read_int32           (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 guint32                g_data_input_stream_read_uint32          (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 gint64                 g_data_input_stream_read_int64           (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 guint64                g_data_input_stream_read_uint64          (GDataInputStream        *stream,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 char *                 g_data_input_stream_read_line            (GDataInputStream        *stream,
-							         gsize                   *length,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 gsize                   *length,
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 void                   g_data_input_stream_read_line_async      (GDataInputStream        *stream,
                                                                  gint                     io_priority,
                                                                  GCancellable            *cancellable,
@@ -112,12 +112,12 @@ char *                 g_data_input_stream_read_line_finish     (GDataInputStrea
                                                                  gsize                   *length,
                                                                  GError                 **error);
 char *                 g_data_input_stream_read_until           (GDataInputStream        *stream,
-							         const gchar             *stop_chars,
-							         gsize                   *length,
-							         GCancellable            *cancellable,
-							         GError                 **error);
+                                                                 const gchar             *stop_chars,
+                                                                 gsize                   *length,
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
 void                   g_data_input_stream_read_until_async     (GDataInputStream        *stream,
-							         const gchar             *stop_chars,
+                                                                 const gchar             *stop_chars,
                                                                  gint                     io_priority,
                                                                  GCancellable            *cancellable,
                                                                  GAsyncReadyCallback      callback,
@@ -127,6 +127,24 @@ char *                 g_data_input_stream_read_until_finish    (GDataInputStrea
                                                                  gsize                   *length,
                                                                  GError                 **error);
 
+char *                 g_data_input_stream_read_upto            (GDataInputStream        *stream,
+                                                                 const gchar             *stop_chars,
+                                                                 gssize                   stop_chars_len,
+                                                                 gsize                   *length,
+                                                                 GCancellable            *cancellable,
+                                                                 GError                 **error);
+void                   g_data_input_stream_read_upto_async      (GDataInputStream        *stream,
+                                                                 const gchar             *stop_chars,
+                                                                 gssize                   stop_chars_len,
+                                                                 gint                     io_priority,
+                                                                 GCancellable            *cancellable,
+                                                                 GAsyncReadyCallback      callback,
+                                                                 gpointer                 user_data);
+char *                 g_data_input_stream_read_upto_finish     (GDataInputStream        *stream,
+                                                                 GAsyncResult            *result,
+                                                                 gsize                   *length,
+                                                                 GError                 **error);
+
 G_END_DECLS
 
 #endif /* __G_DATA_INPUT_STREAM_H__ */
diff --git a/gio/gio.symbols b/gio/gio.symbols
index fa5d623..0e33f61 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -247,6 +247,9 @@ g_data_input_stream_read_line_finish
 g_data_input_stream_read_until
 g_data_input_stream_read_until_async
 g_data_input_stream_read_until_finish
+g_data_input_stream_read_upto
+g_data_input_stream_read_upto_async
+g_data_input_stream_read_upto_finish
 #endif
 #endif
 
diff --git a/gio/tests/data-input-stream.c b/gio/tests/data-input-stream.c
index bc9e4ea..1a34205 100644
--- a/gio/tests/data-input-stream.c
+++ b/gio/tests/data-input-stream.c
@@ -164,7 +164,8 @@ test_read_until (void)
 #define DATA_STRING		" part1 # part2 $ part3 % part4 ^"
 #define DATA_PART_LEN		7    /* number of characters between separators */
 #define DATA_SEP		"#$%^"
-  const int DATA_PARTS_NUM = strlen (DATA_SEP) * REPEATS;
+#define DATA_SEP_LEN            4
+  const int DATA_PARTS_NUM = DATA_SEP_LEN * REPEATS;
   
   base_stream = g_memory_input_stream_new ();
   stream = G_INPUT_STREAM (g_data_input_stream_new (base_stream));
@@ -190,12 +191,65 @@ test_read_until (void)
     }
   g_assert_no_error (error);
   g_assert_cmpint (line, ==, DATA_PARTS_NUM);
-	
-	
+
   g_object_unref (base_stream);
   g_object_unref (stream);
 }
 
+static void
+test_read_upto (void)
+{
+  GInputStream *stream;
+  GInputStream *base_stream;
+  GError *error = NULL;
+  char *data;
+  int line;
+  int i;
+  guchar stop_char;
+
+#undef REPEATS
+#undef DATA_STRING
+#undef DATA_PART_LEN
+#undef DATA_SEP
+#undef DATA_SEP_LEN
+#define REPEATS			10   /* number of rounds */
+#define DATA_STRING		" part1 # part2 $ part3 \0 part4 ^"
+#define DATA_PART_LEN		7    /* number of characters between separators */
+#define DATA_SEP		"#$\0^"
+#define DATA_SEP_LEN            4
+  const int DATA_PARTS_NUM = DATA_SEP_LEN * REPEATS;
+
+  base_stream = g_memory_input_stream_new ();
+  stream = G_INPUT_STREAM (g_data_input_stream_new (base_stream));
+
+  for (i = 0; i < REPEATS; i++)
+    g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (base_stream), DATA_STRING, 32, NULL);
+
+  /*  Test stop characters */
+  error = NULL;
+  data = (char*)1;
+  line = 0;
+  while (data)
+    {
+      gsize length = -1;
+      data = g_data_input_stream_read_upto (G_DATA_INPUT_STREAM (stream), DATA_SEP, DATA_SEP_LEN, &length, NULL, &error);
+      if (data)
+        {
+          g_assert_cmpint (strlen (data), ==, DATA_PART_LEN);
+          g_assert_no_error (error);
+          line++;
+
+          stop_char = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (stream), NULL, &error);
+          g_assert (memchr (DATA_SEP, stop_char, DATA_SEP_LEN) != NULL);
+          g_assert_no_error (error);
+        }
+    }
+  g_assert_no_error (error);
+  g_assert_cmpint (line, ==, DATA_PARTS_NUM);
+
+  g_object_unref (base_stream);
+  g_object_unref (stream);
+}
 enum TestDataType {
   TEST_DATA_BYTE = 0,
   TEST_DATA_INT16,
@@ -367,6 +421,7 @@ main (int   argc,
   g_test_add_func ("/data-input-stream/read-lines-CR-LF", test_read_lines_CR_LF);
   g_test_add_func ("/data-input-stream/read-lines-any", test_read_lines_any);
   g_test_add_func ("/data-input-stream/read-until", test_read_until);
+  g_test_add_func ("/data-input-stream/read-upto", test_read_upto);
   g_test_add_func ("/data-input-stream/read-int", test_read_int);
 
   return g_test_run();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]