Add GBytes variants for GSocket receive methods

The current buffer API is pretty much C-specific, and cannot be
adequately described in a way that is friendly to introspection and
language bindings: the passed buffer is allocated by the caller, but the
written size of the buffer is in the return value.

Using GBytes, we get a better API at the cost of an additional
allocation.
This commit is contained in:
Emmanuele Bassi 2023-10-02 18:22:02 +01:00 committed by Philip Withnall
parent 7688965491
commit 7cb953dab8
2 changed files with 155 additions and 0 deletions

View File

@ -3365,6 +3365,69 @@ g_socket_receive_with_timeout (GSocket *socket,
return ret; return ret;
} }
/**
* g_socket_receive_bytes:
* @socket: a #GSocket
* @size: the number of bytes you want to read from the socket
* @timeout_us: the timeout to wait for, in microseconds, or `-1` to block
* indefinitely
* @cancellable: (nullable): a %GCancellable, or `NULL`
* @error: return location for a #GError, or `NULL`
*
* Receives data (up to @size bytes) from a socket.
*
* This function is a variant of [method@Gio.Socket.receive] which returns a
* [struct@GLib.Bytes] rather than a plain buffer.
*
* Pass `-1` to @timeout_us to block indefinitely until data is received (or
* the connection is closed, or there is an error). Pass `0` to use the default
* timeout from [property@Gio.Socket:timeout], or pass a positive number to wait
* for that many microseconds for data before returning `G_IO_ERROR_TIMED_OUT`.
*
* Returns: (transfer full): a bytes buffer containing the
* received bytes, or `NULL` on error
* Since: 2.80
*/
GBytes *
g_socket_receive_bytes (GSocket *socket,
gsize size,
gint64 timeout_us,
GCancellable *cancellable,
GError **error)
{
guint8 *data;
gssize res;
GBytes *buf;
g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
data = g_new0 (guint8, size);
res = g_socket_receive_with_timeout (socket, data, size, timeout_us, cancellable, error);
if (res < 0)
{
g_free (data);
return NULL;
}
if ((gsize) res == size)
{
buf = g_bytes_new_take (g_steal_pointer (&data), (gsize) res);
}
else
{
GBytes *sub_buf;
buf = g_bytes_new_take (g_steal_pointer (&data), size);
sub_buf = g_bytes_new_from_bytes (buf, 0, (gsize) res);
g_bytes_unref (buf);
buf = g_steal_pointer (&sub_buf);
}
return g_steal_pointer (&buf);
}
/** /**
* g_socket_receive: * g_socket_receive:
* @socket: a #GSocket * @socket: a #GSocket
@ -3446,6 +3509,85 @@ g_socket_receive_with_blocking (GSocket *socket,
blocking ? -1 : 0, cancellable, error); blocking ? -1 : 0, cancellable, error);
} }
/**
* g_socket_receive_bytes_from:
* @socket: a #GSocket
* @address: (out) (optional): return location for a #GSocketAddress
* @size: the number of bytes you want to read from the socket
* @timeout_us: the timeout to wait for, in microseconds, or `-1` to block
* indefinitely
* @cancellable: (nullable): a #GCancellable, or `NULL`
* @error: return location for a #GError, or `NULL`
*
* Receive data (up to @size bytes) from a socket.
*
* This function is a variant of [method@Gio.Socket.receive_from] which returns
* a [struct@GLib.Bytes] rather than a plain buffer.
*
* If @address is non-%NULL then @address will be set equal to the
* source address of the received packet.
*
* The @address is owned by the caller.
*
* Pass `-1` to @timeout_us to block indefinitely until data is received (or
* the connection is closed, or there is an error). Pass `0` to use the default
* timeout from [property@Gio.Socket:timeout], or pass a positive number to wait
* for that many microseconds for data before returning `G_IO_ERROR_TIMED_OUT`.
*
* Returns: (transfer full): a bytes buffer containing the
* received bytes, or `NULL` on error
* Since: 2.80
*/
GBytes *
g_socket_receive_bytes_from (GSocket *socket,
GSocketAddress **address,
gsize size,
gint64 timeout_us,
GCancellable *cancellable,
GError **error)
{
GInputVector v;
gssize res;
GBytes *buf;
g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
g_return_val_if_fail (address == NULL || *address == NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
v.buffer = g_new0 (guint8, size);
v.size = size;
res = g_socket_receive_message_with_timeout (socket,
address,
&v, 1,
NULL, 0, NULL,
timeout_us,
cancellable,
error);
if (res < 0)
{
g_free (v.buffer);
return NULL;
}
if ((gsize) res == size)
{
buf = g_bytes_new_take (g_steal_pointer (&v.buffer), (gsize) res);
}
else
{
GBytes *sub_buf;
buf = g_bytes_new_take (g_steal_pointer (&v.buffer), size);
sub_buf = g_bytes_new_from_bytes (buf, 0, (gsize) res);
g_bytes_unref (buf);
buf = g_steal_pointer (&sub_buf);
}
return g_steal_pointer (&buf);
}
/** /**
* g_socket_receive_from: * g_socket_receive_from:
* @socket: a #GSocket * @socket: a #GSocket

View File

@ -210,6 +210,12 @@ gssize g_socket_receive (GSocket
gsize size, gsize size,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
GIO_AVAILABLE_IN_2_80
GBytes * g_socket_receive_bytes (GSocket *socket,
gsize size,
gint64 timeout_us,
GCancellable *cancellable,
GError **error);
GIO_AVAILABLE_IN_ALL GIO_AVAILABLE_IN_ALL
gssize g_socket_receive_from (GSocket *socket, gssize g_socket_receive_from (GSocket *socket,
GSocketAddress **address, GSocketAddress **address,
@ -217,6 +223,13 @@ gssize g_socket_receive_from (GSocket
gsize size, gsize size,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
GIO_AVAILABLE_IN_2_80
GBytes * g_socket_receive_bytes_from (GSocket *socket,
GSocketAddress **address,
gsize size,
gint64 timeout_us,
GCancellable *cancellable,
GError **error);
GIO_AVAILABLE_IN_ALL GIO_AVAILABLE_IN_ALL
gssize g_socket_send (GSocket *socket, gssize g_socket_send (GSocket *socket,
const gchar *buffer, const gchar *buffer,