*
* Copyright (C) 2006-2007 Red Hat, Inc.
* Copyright (C) 2007 Jürg Billeter
+ * Copyright © 2009 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Author: Alexander Larsson <alexl@redhat.com>
*/
-#include <config.h>
+#include "config.h"
#include "gdatainputstream.h"
+#include "gsimpleasyncresult.h"
+#include "gcancellable.h"
#include "gioenumtypes.h"
+#include "gioerror.h"
#include "glibintl.h"
#include "gioalias.h"
/**
* SECTION:gdatainputstream
* @short_description: Data Input Stream
+ * @include: gio/gio.h
* @see_also: #GInputStream
*
* Data input stream implements #GInputStream and includes functions for
- * reading data directly from an input stream.
+ * reading structured data directly from a binary input stream.
*
**/
const GValue *value,
GParamSpec *pspec)
{
- GDataInputStreamPrivate *priv;
GDataInputStream *dstream;
dstream = G_DATA_INPUT_STREAM (object);
- priv = dstream->priv;
- switch (prop_id)
+ switch (prop_id)
{
case PROP_BYTE_ORDER:
g_data_input_stream_set_byte_order (dstream, g_value_get_enum (value));
*
* Sets the newline type for the @stream.
*
- * TODO: is it valid to set this to G_DATA_STREAM_NEWLINE_TYPE_ANY, or
- * should it always be set to {_LF, _CR, _CR_LF}
+ * Note that using G_DATA_STREAM_NEWLINE_TYPE_ANY is slightly unsafe. If a read
+ * chunk ends in "CR" we must read an additional byte to know if this is "CR" or
+ * "CR LF", and this might block if there is no more data availible.
*
**/
void
return FALSE;
if (res == 0)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unexpected early end-of-stream"));
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Unexpected early end-of-stream"));
return FALSE;
}
}
- /* This should always succeed, since its in the buffer */
+ /* This should always succeed, since it's in the buffer */
res = g_input_stream_read (G_INPUT_STREAM (stream),
buffer, size,
NULL, NULL);
- g_assert (res == size);
+ g_warn_if_fail (res == size);
return TRUE;
}
* Reads an unsigned 8-bit/1-byte value from @stream.
*
* Returns: an unsigned 8-bit/1-byte value read from the @stream or %0
- * if an error occured.
+ * if an error occurred.
**/
guchar
g_data_input_stream_read_byte (GDataInputStream *stream,
- GCancellable *cancellable,
- GError **error)
+ GCancellable *cancellable,
+ GError **error)
{
guchar c;
* Reads a 16-bit/2-byte value from @stream.
*
* In order to get the correct byte order for this read operation,
- * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * see g_data_input_stream_get_byte_order() and g_data_input_stream_set_byte_order().
*
* Returns: a signed 16-bit/2-byte value read from @stream or %0 if
- * an error occured.
+ * an error occurred.
**/
gint16
g_data_input_stream_read_int16 (GDataInputStream *stream,
* Reads an unsigned 16-bit/2-byte value from @stream.
*
* In order to get the correct byte order for this read operation,
- * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * see g_data_input_stream_get_byte_order() and g_data_input_stream_set_byte_order().
*
* Returns: an unsigned 16-bit/2-byte value read from the @stream or %0 if
- * an error occured.
+ * an error occurred.
**/
guint16
g_data_input_stream_read_uint16 (GDataInputStream *stream,
- GCancellable *cancellable,
- GError **error)
+ GCancellable *cancellable,
+ GError **error)
{
guint16 v;
* Reads a signed 32-bit/4-byte value from @stream.
*
* In order to get the correct byte order for this read operation,
- * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * see g_data_input_stream_get_byte_order() and g_data_input_stream_set_byte_order().
*
* 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.
*
* Returns: a signed 32-bit/4-byte value read from the @stream or %0 if
- * an error occured.
+ * an error occurred.
**/
gint32
g_data_input_stream_read_int32 (GDataInputStream *stream,
- GCancellable *cancellable,
- GError **error)
+ GCancellable *cancellable,
+ GError **error)
{
gint32 v;
* Reads an unsigned 32-bit/4-byte value from @stream.
*
* In order to get the correct byte order for this read operation,
- * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * see g_data_input_stream_get_byte_order() and g_data_input_stream_set_byte_order().
*
* 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.
*
* Returns: an unsigned 32-bit/4-byte value read from the @stream or %0 if
- * an error occured.
+ * an error occurred.
**/
guint32
g_data_input_stream_read_uint32 (GDataInputStream *stream,
- GCancellable *cancellable,
- GError **error)
+ GCancellable *cancellable,
+ GError **error)
{
guint32 v;
* Reads a 64-bit/8-byte value from @stream.
*
* In order to get the correct byte order for this read operation,
- * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * see g_data_input_stream_get_byte_order() and g_data_input_stream_set_byte_order().
*
* 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.
*
* Returns: a signed 64-bit/8-byte value read from @stream or %0 if
- * an error occured.
+ * an error occurred.
**/
gint64
g_data_input_stream_read_int64 (GDataInputStream *stream,
* Reads an unsigned 64-bit/8-byte value from @stream.
*
* In order to get the correct byte order for this read operation,
- * see g_data_stream_get_byte_order().
+ * see g_data_input_stream_get_byte_order().
*
* 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.
*
* Returns: an unsigned 64-bit/8-byte read from @stream or %0 if
- * an error occured.
+ * an error occurred.
**/
guint64
g_data_input_stream_read_uint64 (GDataInputStream *stream,
newline_len = 0;
start = checked;
- buffer = g_buffered_input_stream_peek_buffer (bstream, &available) + start;
+ buffer = (const char*)g_buffered_input_stream_peek_buffer (bstream, &available) + start;
end = available;
peeked = end - start;
* @error: #GError for error reporting.
*
* Reads a line from the data input stream.
- *
+ *
* 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.
- *
- * Returns: a string with the line that was read in. Set @length to
- * a #gsize to get the length of the read line. Returns %NULL on an error.
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * Returns: a string with the line that was read in (without the newlines).
+ * Set @length to a #gsize to get the length of the read line.
+ * On an error, it will return %NULL and @error will be set. If there's no
+ * content to read, it will still return %NULL, but @error won't be set.
**/
char *
g_data_input_stream_read_line (GDataInputStream *stream,
NULL, NULL);
if (length)
*length = (gsize)found_pos;
- g_assert (res == found_pos + newline_len);
+ g_warn_if_fail (res == found_pos + newline_len);
line[found_pos] = 0;
return line;
}
-
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;
+ buffer = (const char *)g_buffered_input_stream_peek_buffer (bstream, &available) + start;
end = available;
peeked = end - start;
* @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
- * occurrance of any of the stop characters.
*
- * 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.
- **/
+ * Reads a string from the data input stream, up to the first
+ * occurrence of any of the stop characters.
+ *
+ * Note that, in contrast to g_data_input_stream_read_until_async(),
+ * this function consumes the stop character that it finds.
+ *
+ * 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.
+ */
char *
g_data_input_stream_read_until (GDataInputStream *stream,
const gchar *stop_chars,
NULL, NULL);
if (length)
*length = (gsize)found_pos;
- g_assert (res == found_pos + stop_char_len);
+ g_warn_if_fail (res == found_pos + stop_char_len);
data_until[found_pos] = 0;
return data_until;
}
+typedef struct
+{
+ GDataInputStream *stream;
+ GSimpleAsyncResult *simple;
+ gboolean last_saw_cr;
+ gsize checked;
+ gint io_priority;
+ GCancellable *cancellable;
+
+ gchar *stop_chars;
+ gchar *line;
+ gsize length;
+} GDataInputStreamReadData;
+
+static void
+g_data_input_stream_read_complete (GDataInputStreamReadData *data,
+ gsize read_length,
+ gsize skip_length,
+ gboolean need_idle_dispatch)
+{
+ if (read_length || skip_length)
+ {
+ gssize bytes;
+
+ data->length = read_length;
+ data->line = g_malloc (read_length + 1);
+ data->line[read_length] = '\0';
+
+ /* we already checked the buffer. this shouldn't fail. */
+ bytes = g_input_stream_read (G_INPUT_STREAM (data->stream),
+ data->line, read_length, NULL, NULL);
+ g_assert_cmpint (bytes, ==, read_length);
+
+ bytes = g_input_stream_skip (G_INPUT_STREAM (data->stream),
+ skip_length, NULL, NULL);
+ g_assert_cmpint (bytes, ==, skip_length);
+ }
+
+ if (need_idle_dispatch)
+ g_simple_async_result_complete_in_idle (data->simple);
+ else
+ g_simple_async_result_complete (data->simple);
+
+ g_object_unref (data->simple);
+}
+
+static void
+g_data_input_stream_read_line_ready (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDataInputStreamReadData *data = user_data;
+ gssize found_pos;
+ gint newline_len;
+
+ if (result)
+ /* this is a callback. finish the async call. */
+ {
+ GBufferedInputStream *buffer = G_BUFFERED_INPUT_STREAM (data->stream);
+ GError *error = NULL;
+ gssize bytes;
+
+ bytes = g_buffered_input_stream_fill_finish (buffer, result, &error);
+
+ if (bytes <= 0)
+ {
+ if (bytes < 0)
+ /* stream error. */
+ {
+ g_simple_async_result_set_from_error (data->simple, error);
+ g_error_free (error);
+ data->checked = 0;
+ }
+
+ g_data_input_stream_read_complete (data, data->checked, 0, FALSE);
+ return;
+ }
+
+ /* only proceed if we got more bytes... */
+ }
+
+ if (data->stop_chars)
+ {
+ found_pos = scan_for_chars (data->stream,
+ &data->checked,
+ data->stop_chars);
+ newline_len = 0;
+ }
+ else
+ found_pos = scan_for_newline (data->stream, &data->checked,
+ &data->last_saw_cr, &newline_len);
+
+ if (found_pos == -1)
+ /* didn't find a full line; need to buffer some more bytes */
+ {
+ GBufferedInputStream *buffer = G_BUFFERED_INPUT_STREAM (data->stream);
+ gsize size;
+
+ size = g_buffered_input_stream_get_buffer_size (buffer);
+
+ if (g_buffered_input_stream_get_available (buffer) == size)
+ /* need to grow the buffer */
+ g_buffered_input_stream_set_buffer_size (buffer, size * 2);
+
+ /* try again */
+ g_buffered_input_stream_fill_async (buffer, -1, data->io_priority,
+ data->cancellable,
+ g_data_input_stream_read_line_ready,
+ user_data);
+ }
+ else
+ {
+ /* read the line and the EOL. no error is possible. */
+ g_data_input_stream_read_complete (data, found_pos,
+ newline_len, result == NULL);
+ }
+}
+
+static void
+g_data_input_stream_read_data_free (gpointer user_data)
+{
+ GDataInputStreamReadData *data = user_data;
+
+ /* we don't hold a ref to ->simple because it keeps a ref to us.
+ * we are called because it is being finalized.
+ */
+
+ g_free (data->stop_chars);
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->line);
+ g_slice_free (GDataInputStreamReadData, data);
+}
+
+static void
+g_data_input_stream_read_async (GDataInputStream *stream,
+ const gchar *stop_chars,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer source_tag)
+{
+ GDataInputStreamReadData *data;
+
+ data = g_slice_new (GDataInputStreamReadData);
+ data->stream = stream;
+ if (cancellable)
+ g_object_ref (cancellable);
+ data->cancellable = cancellable;
+ data->stop_chars = g_strdup (stop_chars);
+ data->io_priority = io_priority;
+ data->last_saw_cr = FALSE;
+ data->checked = 0;
+ data->line = NULL;
+
+ data->simple = g_simple_async_result_new (G_OBJECT (stream), callback,
+ user_data, source_tag);
+ g_simple_async_result_set_op_res_gpointer (data->simple, data,
+ g_data_input_stream_read_data_free);
+ g_data_input_stream_read_line_ready (NULL, NULL, data);
+}
+
+static gchar *
+g_data_input_stream_read_finish (GDataInputStream *stream,
+ GAsyncResult *result,
+ gsize *length,
+ GError **error)
+{
+ GDataInputStreamReadData *data;
+ GSimpleAsyncResult *simple;
+ gchar *line;
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ data = g_simple_async_result_get_op_res_gpointer (simple);
+
+ line = data->line;
+ data->line = NULL;
+
+ if (length && line)
+ *length = data->length;
+
+ return line;
+}
+
+/**
+ * g_data_input_stream_read_line_async:
+ * @stream: a given #GDataInputStream.
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ * of the request.
+ * @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_line(). It is
+ * an error to have two outstanding calls to this function.
+ *
+ * When the operation is finished, @callback will be called. You
+ * can then call g_data_input_stream_read_line_finish() to get
+ * the result of the operation.
+ *
+ * Since: 2.20
+ */
+void
+g_data_input_stream_read_line_async (GDataInputStream *stream,
+ 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_data_input_stream_read_async (stream, NULL, io_priority,
+ cancellable, callback, user_data,
+ g_data_input_stream_read_line_async);
+}
+
+/**
+ * g_data_input_stream_read_until_async:
+ * @stream: a given #GDataInputStream.
+ * @stop_chars: characters to terminate the read.
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ * of the request.
+ * @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_until().
+ * It is an error to have two outstanding calls to this function.
+ *
+ * Note that, in contrast to g_data_input_stream_read_until(),
+ * this function does not consume the stop character that it finds. You
+ * must read it for yourself.
+ *
+ * When the operation is finished, @callback will be called. You
+ * can then call g_data_input_stream_read_until_finish() to get
+ * the result of the operation.
+ *
+ * Since: 2.20
+ */
+void
+g_data_input_stream_read_until_async (GDataInputStream *stream,
+ const gchar *stop_chars,
+ 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, io_priority,
+ cancellable, callback, user_data,
+ g_data_input_stream_read_until_async);
+}
+
+/**
+ * g_data_input_stream_read_line_finish:
+ * @stream: a given #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_line_async().
+ *
+ * Returns: a string with the line that was read in (without the newlines).
+ * Set @length to a #gsize to get the length of the read line.
+ * On an error, it will return %NULL and @error will be set. If there's no
+ * content to read, it will still return %NULL, but @error won't be set.
+ *
+ * Since: 2.20
+ */
+gchar *
+g_data_input_stream_read_line_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_line_async), NULL);
+
+ return g_data_input_stream_read_finish (stream, result, length, error);
+}
+
+/**
+ * g_data_input_stream_read_until_finish:
+ * @stream: a given #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_until_async().
+ *
+ * Since: 2.20
+ *
+ * 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.
+ */
+gchar *
+g_data_input_stream_read_until_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_until_async), NULL);
+
+ return g_data_input_stream_read_finish (stream, result, length, error);
+}
+
+
#define __G_DATA_INPUT_STREAM_C__
#include "gioaliasdef.c"