diff options
author | Sebastian Dröge <sebastian@centricular.com> | 2018-09-13 21:07:39 +0300 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2019-01-24 16:25:40 +0200 |
commit | c39264d35b3911141b3a0d972e63411ab753b7d2 (patch) | |
tree | c9f2526252146a7e9218bc90a1db21117e611e22 | |
parent | 89da9eb6c0291262f0158e5c6e21b0414790b508 (diff) | |
download | glib-c39264d35b3911141b3a0d972e63411ab753b7d2.tar.gz |
Implement GOutputStream::writev_fn() for GLocalFileOutputStream on UNIX
-rw-r--r-- | gio/glocalfileoutputstream.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c index 57d2d5dfe..6d44989bf 100644 --- a/gio/glocalfileoutputstream.c +++ b/gio/glocalfileoutputstream.c @@ -38,6 +38,7 @@ #ifdef G_OS_UNIX #include <unistd.h> #include "gfiledescriptorbased.h" +#include <sys/uio.h> #endif #include "glib-private.h" @@ -93,6 +94,14 @@ static gssize g_local_file_output_stream_write (GOutputStream *s gsize count, GCancellable *cancellable, GError **error); +#ifdef G_OS_UNIX +static gboolean g_local_file_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error); +#endif static gboolean g_local_file_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error); @@ -142,6 +151,9 @@ g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *klass) gobject_class->finalize = g_local_file_output_stream_finalize; stream_class->write_fn = g_local_file_output_stream_write; +#ifdef G_OS_UNIX + stream_class->writev_fn = g_local_file_output_stream_writev; +#endif stream_class->close_fn = g_local_file_output_stream_close; file_stream_class->query_info = g_local_file_output_stream_query_info; file_stream_class->get_etag = g_local_file_output_stream_get_etag; @@ -203,6 +215,89 @@ g_local_file_output_stream_write (GOutputStream *stream, return res; } +/* On Windows there is no equivalent API for files. The closest API to that is + * WriteFileGather() but it is useless in general: it requires, among other + * things, that each chunk is the size of a whole page and in memory aligned + * to a page. We can't possibly guarantee that in GLib. + */ +#ifdef G_OS_UNIX +/* Macro to check if struct iovec and GOutputVector have the same ABI */ +#define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \ + sizeof ((struct iovec *) 0)->iov_base == sizeof ((GOutputVector *) 0)->buffer && \ + G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \ + sizeof ((struct iovec *) 0)->iov_len == sizeof((GOutputVector *) 0)->size && \ + G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size)) + +static gboolean +g_local_file_output_stream_writev (GOutputStream *stream, + const GOutputVector *vectors, + gsize n_vectors, + gsize *bytes_written, + GCancellable *cancellable, + GError **error) +{ + GLocalFileOutputStream *file; + gssize res; + struct iovec *iov; + + if (bytes_written) + *bytes_written = 0; + + /* Clamp to G_MAXINT as writev() takes an integer for the number of vectors. + * We handle this like a short write in this case + */ + if (n_vectors > G_MAXINT) + n_vectors = G_MAXINT; + + file = G_LOCAL_FILE_OUTPUT_STREAM (stream); + + if (G_OUTPUT_VECTOR_IS_IOVEC) + { + /* ABI is compatible */ + iov = (struct iovec *) vectors; + } + else + { + gsize i; + + /* ABI is incompatible */ + iov = g_newa (struct iovec, n_vectors); + for (i = 0; i < n_vectors; i++) + { + iov[i].iov_base = (void *)vectors[i].buffer; + iov[i].iov_len = vectors[i].size; + } + } + + while (1) + { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + res = writev (file->priv->fd, iov, n_vectors); + if (res == -1) + { + int errsv = errno; + + if (errsv == EINTR) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error writing to file: %s"), + g_strerror (errsv)); + } + else if (bytes_written) + { + *bytes_written = res; + } + + break; + } + + return res != -1; +} +#endif + void _g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out, gboolean do_close) |