gio: implement GPollableInput/OutputStream in more stream types

Implement GPollableInputStream in GMemoryInputStream and
GConverterInputStream, and likewise implement GPollableOutputStream in
the corresponding output streams.

https://bugzilla.gnome.org/show_bug.cgi?id=673997
This commit is contained in:
Dan Winship 2012-02-06 15:08:08 -05:00
parent 111ba203c2
commit 82ec4dcaed
5 changed files with 508 additions and 36 deletions

View File

@ -25,6 +25,7 @@
#include <string.h> #include <string.h>
#include "gconverterinputstream.h" #include "gconverterinputstream.h"
#include "gpollableinputstream.h"
#include "gsimpleasyncresult.h" #include "gsimpleasyncresult.h"
#include "gcancellable.h" #include "gcancellable.h"
#include "gioenumtypes.h" #include "gioenumtypes.h"
@ -41,6 +42,8 @@
* Converter input stream implements #GInputStream and allows * Converter input stream implements #GInputStream and allows
* conversion of data of various types during reading. * conversion of data of various types during reading.
* *
* As of GLib 2.34, #GConverterInputStream implements
* #GPollableInputStream.
**/ **/
#define INITIAL_BUFFER_SIZE 4096 #define INITIAL_BUFFER_SIZE 4096
@ -55,6 +58,7 @@ typedef struct {
struct _GConverterInputStreamPrivate { struct _GConverterInputStreamPrivate {
gboolean at_input_end; gboolean at_input_end;
gboolean finished; gboolean finished;
gboolean need_input;
GConverter *converter; GConverter *converter;
Buffer input_buffer; Buffer input_buffer;
Buffer converted_buffer; Buffer converted_buffer;
@ -80,9 +84,24 @@ static gssize g_converter_input_stream_read (GInputStream *stream,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
G_DEFINE_TYPE (GConverterInputStream, static gboolean g_converter_input_stream_can_poll (GPollableInputStream *stream);
g_converter_input_stream, static gboolean g_converter_input_stream_is_readable (GPollableInputStream *stream);
G_TYPE_FILTER_INPUT_STREAM) static gssize g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
void *buffer,
gsize size,
GError **error);
static GSource *g_converter_input_stream_create_source (GPollableInputStream *stream,
GCancellable *cancellable);
static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GConverterInputStream,
g_converter_input_stream,
G_TYPE_FILTER_INPUT_STREAM,
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
g_converter_input_stream_pollable_iface_init);
)
static void static void
g_converter_input_stream_class_init (GConverterInputStreamClass *klass) g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
@ -112,6 +131,15 @@ g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
} }
static void
g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
{
iface->can_poll = g_converter_input_stream_can_poll;
iface->is_readable = g_converter_input_stream_is_readable;
iface->read_nonblocking = g_converter_input_stream_read_nonblocking;
iface->create_source = g_converter_input_stream_create_source;
}
static void static void
g_converter_input_stream_finalize (GObject *object) g_converter_input_stream_finalize (GObject *object)
{ {
@ -320,6 +348,7 @@ buffer_ensure_space (Buffer *buffer,
static gssize static gssize
fill_input_buffer (GConverterInputStream *stream, fill_input_buffer (GConverterInputStream *stream,
gsize at_least_size, gsize at_least_size,
gboolean blocking,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
@ -332,25 +361,30 @@ fill_input_buffer (GConverterInputStream *stream,
buffer_ensure_space (&priv->input_buffer, at_least_size); buffer_ensure_space (&priv->input_buffer, at_least_size);
base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
nread = g_input_stream_read (base_stream, nread = g_pollable_stream_read (base_stream,
priv->input_buffer.data + priv->input_buffer.end, priv->input_buffer.data + priv->input_buffer.end,
buffer_tailspace (&priv->input_buffer), buffer_tailspace (&priv->input_buffer),
cancellable, blocking,
error); cancellable,
error);
if (nread > 0) if (nread > 0)
priv->input_buffer.end += nread; {
priv->input_buffer.end += nread;
priv->need_input = FALSE;
}
return nread; return nread;
} }
static gssize static gssize
g_converter_input_stream_read (GInputStream *stream, read_internal (GInputStream *stream,
void *buffer, void *buffer,
gsize count, gsize count,
GCancellable *cancellable, gboolean blocking,
GError **error) GCancellable *cancellable,
GError **error)
{ {
GConverterInputStream *cstream; GConverterInputStream *cstream;
GConverterInputStreamPrivate *priv; GConverterInputStreamPrivate *priv;
@ -389,7 +423,7 @@ g_converter_input_stream_read (GInputStream *stream,
total_bytes_read == 0 && total_bytes_read == 0 &&
!priv->at_input_end) !priv->at_input_end)
{ {
nread = fill_input_buffer (cstream, count, cancellable, error); nread = fill_input_buffer (cstream, count, blocking, cancellable, error);
if (nread < 0) if (nread < 0)
return -1; return -1;
if (nread == 0) if (nread == 0)
@ -497,6 +531,7 @@ g_converter_input_stream_read (GInputStream *stream,
my_error2 = NULL; my_error2 = NULL;
nread = fill_input_buffer (cstream, nread = fill_input_buffer (cstream,
buffer_data_size (&priv->input_buffer) + 4096, buffer_data_size (&priv->input_buffer) + 4096,
blocking,
cancellable, cancellable,
&my_error2); &my_error2);
if (nread < 0) if (nread < 0)
@ -504,6 +539,7 @@ g_converter_input_stream_read (GInputStream *stream,
/* Can't read any more data, return that error */ /* Can't read any more data, return that error */
g_error_free (my_error); g_error_free (my_error);
g_propagate_error (error, my_error2); g_propagate_error (error, my_error2);
priv->need_input = TRUE;
return -1; return -1;
} }
else if (nread == 0) else if (nread == 0)
@ -536,6 +572,70 @@ g_converter_input_stream_read (GInputStream *stream,
g_assert_not_reached (); g_assert_not_reached ();
} }
static gssize
g_converter_input_stream_read (GInputStream *stream,
void *buffer,
gsize count,
GCancellable *cancellable,
GError **error)
{
return read_internal (stream, buffer, count, TRUE, cancellable, error);
}
static gboolean
g_converter_input_stream_can_poll (GPollableInputStream *stream)
{
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
return (G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)));
}
static gboolean
g_converter_input_stream_is_readable (GPollableInputStream *stream)
{
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream);
if (buffer_data_size (&cstream->priv->converted_buffer))
return TRUE;
else if (buffer_data_size (&cstream->priv->input_buffer) &&
!cstream->priv->need_input)
return TRUE;
else
return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream));
}
static gssize
g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
void *buffer,
gsize count,
GError **error)
{
return read_internal (G_INPUT_STREAM (stream), buffer, count,
FALSE, NULL, error);
}
static GSource *
g_converter_input_stream_create_source (GPollableInputStream *stream,
GCancellable *cancellable)
{
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
GSource *base_source, *pollable_source;
if (g_pollable_input_stream_is_readable (stream))
base_source = g_timeout_source_new (0);
else
base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL);
pollable_source = g_pollable_source_new_full (stream, base_source,
cancellable);
g_source_unref (base_source);
return pollable_source;
}
/** /**
* g_converter_input_stream_get_converter: * g_converter_input_stream_get_converter:
* @converter_stream: a #GConverterInputStream * @converter_stream: a #GConverterInputStream

View File

@ -25,6 +25,7 @@
#include <string.h> #include <string.h>
#include "gconverteroutputstream.h" #include "gconverteroutputstream.h"
#include "gpollableoutputstream.h"
#include "gsimpleasyncresult.h" #include "gsimpleasyncresult.h"
#include "gcancellable.h" #include "gcancellable.h"
#include "gioenumtypes.h" #include "gioenumtypes.h"
@ -41,6 +42,8 @@
* Converter output stream implements #GOutputStream and allows * Converter output stream implements #GOutputStream and allows
* conversion of data of various types during reading. * conversion of data of various types during reading.
* *
* As of GLib 2.34, #GConverterOutputStream implements
* #GPollableOutputStream.
**/ **/
#define INITIAL_BUFFER_SIZE 4096 #define INITIAL_BUFFER_SIZE 4096
@ -96,9 +99,24 @@ static gboolean g_converter_output_stream_flush (GOutputStream *stream,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
G_DEFINE_TYPE (GConverterOutputStream, static gboolean g_converter_output_stream_can_poll (GPollableOutputStream *stream);
g_converter_output_stream, static gboolean g_converter_output_stream_is_writable (GPollableOutputStream *stream);
G_TYPE_FILTER_OUTPUT_STREAM) static gssize g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
const void *buffer,
gsize size,
GError **error);
static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable);
static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
g_converter_output_stream,
G_TYPE_FILTER_OUTPUT_STREAM,
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
g_converter_output_stream_pollable_iface_init);
)
static void static void
g_converter_output_stream_class_init (GConverterOutputStreamClass *klass) g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
@ -129,6 +147,15 @@ g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
} }
static void
g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
{
iface->can_poll = g_converter_output_stream_can_poll;
iface->is_writable = g_converter_output_stream_is_writable;
iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
iface->create_source = g_converter_output_stream_create_source;
}
static void static void
g_converter_output_stream_finalize (GObject *object) g_converter_output_stream_finalize (GObject *object)
{ {
@ -339,7 +366,7 @@ buffer_append (Buffer *buffer,
static gboolean static gboolean
flush_buffer (GConverterOutputStream *stream, flush_buffer (GConverterOutputStream *stream,
Buffer *buffer, gboolean blocking,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
@ -356,12 +383,13 @@ flush_buffer (GConverterOutputStream *stream,
available = buffer_data_size (&priv->converted_buffer); available = buffer_data_size (&priv->converted_buffer);
if (available > 0) if (available > 0)
{ {
res = g_output_stream_write_all (base_stream, res = g_pollable_stream_write_all (base_stream,
buffer_data (&priv->converted_buffer), buffer_data (&priv->converted_buffer),
available, available,
&nwritten, blocking,
cancellable, &nwritten,
error); cancellable,
error);
buffer_consumed (&priv->converted_buffer, nwritten); buffer_consumed (&priv->converted_buffer, nwritten);
return res; return res;
} }
@ -370,11 +398,12 @@ flush_buffer (GConverterOutputStream *stream,
static gssize static gssize
g_converter_output_stream_write (GOutputStream *stream, write_internal (GOutputStream *stream,
const void *buffer, const void *buffer,
gsize count, gsize count,
GCancellable *cancellable, gboolean blocking,
GError **error) GCancellable *cancellable,
GError **error)
{ {
GConverterOutputStream *cstream; GConverterOutputStream *cstream;
GConverterOutputStreamPrivate *priv; GConverterOutputStreamPrivate *priv;
@ -392,7 +421,7 @@ g_converter_output_stream_write (GOutputStream *stream,
/* Write out all available pre-converted data and fail if /* Write out all available pre-converted data and fail if
not possible */ not possible */
if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error)) if (!flush_buffer (cstream, blocking, cancellable, error))
return -1; return -1;
if (priv->finished) if (priv->finished)
@ -499,11 +528,21 @@ g_converter_output_stream_write (GOutputStream *stream,
even if writing this to the base stream fails. If it does we'll just even if writing this to the base stream fails. If it does we'll just
stop early and report this error when we try again on the next stop early and report this error when we try again on the next
write call. */ write call. */
flush_buffer (cstream, &priv->converted_buffer, cancellable, NULL); flush_buffer (cstream, blocking, cancellable, NULL);
return retval; return retval;
} }
static gssize
g_converter_output_stream_write (GOutputStream *stream,
const void *buffer,
gsize count,
GCancellable *cancellable,
GError **error)
{
return write_internal (stream, buffer, count, TRUE, cancellable, error);
}
static gboolean static gboolean
g_converter_output_stream_flush (GOutputStream *stream, g_converter_output_stream_flush (GOutputStream *stream,
GCancellable *cancellable, GCancellable *cancellable,
@ -525,7 +564,7 @@ g_converter_output_stream_flush (GOutputStream *stream,
/* Write out all available pre-converted data and fail if /* Write out all available pre-converted data and fail if
not possible */ not possible */
if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error)) if (!flush_buffer (cstream, TRUE, cancellable, error))
return FALSE; return FALSE;
/* Ensure we have *some* initial target space */ /* Ensure we have *some* initial target space */
@ -590,12 +629,54 @@ g_converter_output_stream_flush (GOutputStream *stream,
} }
/* Now write all converted data to base stream */ /* Now write all converted data to base stream */
if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error)) if (!flush_buffer (cstream, TRUE, cancellable, error))
return FALSE; return FALSE;
return TRUE; return TRUE;
} }
static gboolean
g_converter_output_stream_can_poll (GPollableOutputStream *stream)
{
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
}
static gboolean
g_converter_output_stream_is_writable (GPollableOutputStream *stream)
{
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
}
static gssize
g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
const void *buffer,
gsize count,
GError **error)
{
return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
NULL, error);
}
static GSource *
g_converter_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable)
{
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
GSource *base_source, *pollable_source;
base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
pollable_source = g_pollable_source_new_full (stream, base_source,
cancellable);
g_source_unref (base_source);
return pollable_source;
}
/** /**
* g_converter_output_stream_get_converter: * g_converter_output_stream_get_converter:
* @converter_stream: a #GConverterOutputStream * @converter_stream: a #GConverterOutputStream

View File

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include "gmemoryinputstream.h" #include "gmemoryinputstream.h"
#include "gpollableinputstream.h"
#include "ginputstream.h" #include "ginputstream.h"
#include "gseekable.h" #include "gseekable.h"
#include "string.h" #include "string.h"
@ -39,6 +40,8 @@
* #GMemoryInputStream is a class for using arbitrary * #GMemoryInputStream is a class for using arbitrary
* memory chunks as input for GIO streaming input operations. * memory chunks as input for GIO streaming input operations.
* *
* As of GLib 2.34, #GMemoryInputStream implements
* #GPollableInputStream.
*/ */
typedef struct _Chunk Chunk; typedef struct _Chunk Chunk;
@ -108,11 +111,20 @@ static gboolean g_memory_input_stream_truncate (GSeekable *seek
goffset offset, goffset offset,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
static void g_memory_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
static gboolean g_memory_input_stream_is_readable (GPollableInputStream *stream);
static GSource *g_memory_input_stream_create_source (GPollableInputStream *stream,
GCancellable *cancellable);
static void g_memory_input_stream_finalize (GObject *object); static void g_memory_input_stream_finalize (GObject *object);
G_DEFINE_TYPE_WITH_CODE (GMemoryInputStream, g_memory_input_stream, G_TYPE_INPUT_STREAM, G_DEFINE_TYPE_WITH_CODE (GMemoryInputStream, g_memory_input_stream, G_TYPE_INPUT_STREAM,
G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
g_memory_input_stream_seekable_iface_init)) g_memory_input_stream_seekable_iface_init);
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
g_memory_input_stream_pollable_iface_init);
)
static void static void
@ -174,6 +186,13 @@ g_memory_input_stream_seekable_iface_init (GSeekableIface *iface)
iface->truncate_fn = g_memory_input_stream_truncate; iface->truncate_fn = g_memory_input_stream_truncate;
} }
static void
g_memory_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
{
iface->is_readable = g_memory_input_stream_is_readable;
iface->create_source = g_memory_input_stream_create_source;
}
static void static void
g_memory_input_stream_init (GMemoryInputStream *stream) g_memory_input_stream_init (GMemoryInputStream *stream)
{ {
@ -526,3 +545,23 @@ g_memory_input_stream_truncate (GSeekable *seekable,
_("Cannot truncate GMemoryInputStream")); _("Cannot truncate GMemoryInputStream"));
return FALSE; return FALSE;
} }
static gboolean
g_memory_input_stream_is_readable (GPollableInputStream *stream)
{
return TRUE;
}
static GSource *
g_memory_input_stream_create_source (GPollableInputStream *stream,
GCancellable *cancellable)
{
GSource *base_source, *pollable_source;
base_source = g_timeout_source_new (0);
pollable_source = g_pollable_source_new_full (stream, base_source,
cancellable);
g_source_unref (base_source);
return pollable_source;
}

View File

@ -25,6 +25,7 @@
#include "config.h" #include "config.h"
#include "gmemoryoutputstream.h" #include "gmemoryoutputstream.h"
#include "goutputstream.h" #include "goutputstream.h"
#include "gpollableoutputstream.h"
#include "gseekable.h" #include "gseekable.h"
#include "gsimpleasyncresult.h" #include "gsimpleasyncresult.h"
#include "gioerror.h" #include "gioerror.h"
@ -41,6 +42,8 @@
* #GMemoryOutputStream is a class for using arbitrary * #GMemoryOutputStream is a class for using arbitrary
* memory chunks as output for GIO streaming output operations. * memory chunks as output for GIO streaming output operations.
* *
* As of GLib 2.34, #GMemoryOutputStream implements
* #GPollableOutputStream.
*/ */
#define MIN_ARRAY_SIZE 16 #define MIN_ARRAY_SIZE 16
@ -119,9 +122,17 @@ static gboolean g_memory_output_stream_truncate (GSeekable *see
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
static gboolean g_memory_output_stream_is_writable (GPollableOutputStream *stream);
static GSource *g_memory_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable);
static void g_memory_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GMemoryOutputStream, g_memory_output_stream, G_TYPE_OUTPUT_STREAM, G_DEFINE_TYPE_WITH_CODE (GMemoryOutputStream, g_memory_output_stream, G_TYPE_OUTPUT_STREAM,
G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
g_memory_output_stream_seekable_iface_init)) g_memory_output_stream_seekable_iface_init);
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
g_memory_output_stream_pollable_iface_init))
static void static void
@ -224,6 +235,13 @@ g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass)
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
} }
static void
g_memory_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
{
iface->is_writable = g_memory_output_stream_is_writable;
iface->create_source = g_memory_output_stream_create_source;
}
static void static void
g_memory_output_stream_set_property (GObject *object, g_memory_output_stream_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -800,3 +818,23 @@ g_memory_output_stream_truncate (GSeekable *seekable,
return TRUE; return TRUE;
} }
static gboolean
g_memory_output_stream_is_writable (GPollableOutputStream *stream)
{
return TRUE;
}
static GSource *
g_memory_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable)
{
GSource *base_source, *pollable_source;
base_source = g_timeout_source_new (0);
pollable_source = g_pollable_source_new_full (stream, base_source,
cancellable);
g_source_unref (base_source);
return pollable_source;
}

View File

@ -724,6 +724,219 @@ test_charset (gconstpointer data)
g_object_unref (conv); g_object_unref (conv);
} }
static void
client_connected (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSocketClient *client = G_SOCKET_CLIENT (source);
GSocketConnection **conn = user_data;
GError *error = NULL;
*conn = g_socket_client_connect_finish (client, result, &error);
g_assert_no_error (error);
}
static void
server_connected (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSocketListener *listener = G_SOCKET_LISTENER (source);
GSocketConnection **conn = user_data;
GError *error = NULL;
*conn = g_socket_listener_accept_finish (listener, result, NULL, &error);
g_assert_no_error (error);
}
static void
make_socketpair (GIOStream **left,
GIOStream **right)
{
GInetAddress *iaddr;
GSocketAddress *saddr, *effective_address;
GSocketListener *listener;
GSocketClient *client;
GError *error = NULL;
GSocketConnection *client_conn = NULL, *server_conn = NULL;
iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
saddr = g_inet_socket_address_new (iaddr, 0);
g_object_unref (iaddr);
listener = g_socket_listener_new ();
g_socket_listener_add_address (listener, saddr,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_TCP,
NULL,
&effective_address,
&error);
g_assert_no_error (error);
g_object_unref (saddr);
client = g_socket_client_new ();
g_socket_client_connect_async (client,
G_SOCKET_CONNECTABLE (effective_address),
NULL, client_connected, &client_conn);
g_socket_listener_accept_async (listener, NULL,
server_connected, &server_conn);
while (!client_conn || !server_conn)
g_main_context_iteration (NULL, TRUE);
g_object_unref (client);
g_object_unref (listener);
g_object_unref (effective_address);
*left = G_IO_STREAM (client_conn);
*right = G_IO_STREAM (server_conn);
}
static void
test_converter_pollable (void)
{
GIOStream *left, *right;
guint8 *converted, *inptr;
guint8 *expanded, *outptr, *expanded_end;
gsize n_read, expanded_size;
gsize total_read;
gssize res;
gboolean is_readable;
GConverterResult cres;
GInputStream *cstream;
GPollableInputStream *pollable_in;
GOutputStream *socket_out, *mem_out, *cstream_out;
GPollableOutputStream *pollable_out;
GConverter *expander, *compressor;
GError *error;
int i;
expander = g_expander_converter_new ();
expanded = g_malloc (100*1000); /* Large enough */
cres = g_converter_convert (expander,
unexpanded_data, sizeof(unexpanded_data),
expanded, 100*1000,
G_CONVERTER_INPUT_AT_END,
&n_read, &expanded_size, NULL);
g_assert (cres == G_CONVERTER_FINISHED);
g_assert (n_read == 11);
g_assert (expanded_size == 41030);
expanded_end = expanded + expanded_size;
make_socketpair (&left, &right);
compressor = g_compressor_converter_new ();
converted = g_malloc (100*1000); /* Large enough */
cstream = g_converter_input_stream_new (g_io_stream_get_input_stream (left),
compressor);
pollable_in = G_POLLABLE_INPUT_STREAM (cstream);
g_assert (g_pollable_input_stream_can_poll (pollable_in));
socket_out = g_io_stream_get_output_stream (right);
total_read = 0;
outptr = expanded;
inptr = converted;
while (TRUE)
{
error = NULL;
if (outptr < expanded_end)
{
res = g_output_stream_write (socket_out,
outptr,
MIN (1000, (expanded_end - outptr)),
NULL, &error);
g_assert_cmpint (res, >, 0);
outptr += res;
}
else if (socket_out)
{
g_object_unref (right);
socket_out = NULL;
}
is_readable = g_pollable_input_stream_is_readable (pollable_in);
res = g_pollable_input_stream_read_nonblocking (pollable_in,
inptr, 1,
NULL, &error);
/* is_readable can be a false positive, but not a false negative */
if (!is_readable)
g_assert_cmpint (res, ==, -1);
/* After closing the write end, we can't get WOULD_BLOCK any more */
if (!socket_out)
g_assert_cmpint (res, !=, -1);
if (res == -1)
{
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK);
g_error_free (error);
continue;
}
if (res == 0)
break;
inptr += res;
total_read += res;
}
g_assert (total_read == n_read - 1); /* Last 2 zeros are combined */
g_assert (memcmp (converted, unexpanded_data, total_read) == 0);
g_object_unref (cstream);
g_object_unref (left);
g_converter_reset (compressor);
/* This doesn't actually test the behavior on
* G_IO_ERROR_WOULD_BLOCK; to do that we'd need to implement a
* custom GOutputStream that we could control blocking on.
*/
mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
cstream_out = g_converter_output_stream_new (mem_out, compressor);
g_object_unref (mem_out);
pollable_out = G_POLLABLE_OUTPUT_STREAM (cstream_out);
for (i = 0; i < expanded_size; i++)
{
error = NULL;
res = g_pollable_output_stream_write_nonblocking (pollable_out,
expanded + i, 1,
NULL, &error);
g_assert (res != -1);
if (res == 0)
{
g_assert (i == expanded_size -1);
break;
}
g_assert (res == 1);
}
g_output_stream_close (cstream_out, NULL, NULL);
g_assert (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out)) == n_read - 1); /* Last 2 zeros are combined */
g_assert (memcmp (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mem_out)),
unexpanded_data,
g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mem_out))) == 0);
g_object_unref (cstream_out);
g_free (expanded);
g_free (converted);
g_object_unref (expander);
g_object_unref (compressor);
}
int int
main (int argc, main (int argc,
char *argv[]) char *argv[])
@ -759,6 +972,7 @@ main (int argc,
for (i = 0; i < G_N_ELEMENTS (charset_tests); i++) for (i = 0; i < G_N_ELEMENTS (charset_tests); i++)
g_test_add_data_func (charset_tests[i].path, &charset_tests[i], test_charset); g_test_add_data_func (charset_tests[i].path, &charset_tests[i], test_charset);
g_test_add_func ("/converter-stream/pollable", test_converter_pollable);
return g_test_run(); return g_test_run();
} }