[glib: 4/15] Implement GOutputStream::writev_fn() for GLocalFileOutputStream on UNIX



commit c39264d35b3911141b3a0d972e63411ab753b7d2
Author: Sebastian Dröge <sebastian centricular com>
Date:   Thu Sep 13 21:07:39 2018 +0300

    Implement GOutputStream::writev_fn() for GLocalFileOutputStream on UNIX

 gio/glocalfileoutputstream.c | 95 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
---
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)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]