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.
This commit is contained in:
Ryan Lortie
2010-03-23 01:12:01 -05:00
parent 0ecfc6e1f9
commit 2e78d07f86
5 changed files with 312 additions and 78 deletions

View File

@@ -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);
}