Merge "libcutils: add multi-buffer socket send." am: d95ecfc432
am: d56499b21d

* commit 'd56499b21d1411a7c799cdad316fb2138db26a7c':
  libcutils: add multi-buffer socket send.
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
index 6cffe12..cb9b3ff 100644
--- a/include/cutils/sockets.h
+++ b/include/cutils/sockets.h
@@ -30,12 +30,15 @@
 
 typedef int  socklen_t;
 typedef SOCKET cutils_socket_t;
+typedef WSABUF cutils_socket_buffer_t;
 
 #else
 
 #include <sys/socket.h>
+#include <sys/uio.h>
 
 typedef int cutils_socket_t;
+typedef struct iovec cutils_socket_buffer_t;
 #define INVALID_SOCKET (-1)
 
 #endif
@@ -137,6 +140,28 @@
 int socket_get_local_port(cutils_socket_t sock);
 
 /*
+ * Sends to a socket from multiple buffers; wraps writev() on Unix or WSASend()
+ * on Windows. This can give significant speedup compared to calling send()
+ * multiple times.
+ *
+ * Because Unix and Windows use different structs to hold buffers, we also
+ * need a generic function to set up the buffers.
+ *
+ * Example usage:
+ *   cutils_socket_buffer_t buffers[2] = {
+ *       make_cutils_socket_buffer(data0, len0),
+ *       make_cutils_socket_buffer(data1, len1)
+ *   };
+ *   socket_send_buffers(sock, buffers, 2);
+ *
+ * Returns the number of bytes written or -1 on error.
+ */
+cutils_socket_buffer_t make_cutils_socket_buffer(void* data, size_t length);
+ssize_t socket_send_buffers(cutils_socket_t sock,
+                            cutils_socket_buffer_t* buffers,
+                            size_t num_buffers);
+
+/*
  * socket_peer_is_trusted - Takes a socket which is presumed to be a
  * connected local socket (e.g. AF_LOCAL) and returns whether the peer
  * (the userid that owns the process on the other end of that socket)
diff --git a/libcutils/sockets_unix.c b/libcutils/sockets_unix.c
index 5eddc4b..3e7cea0 100644
--- a/libcutils/sockets_unix.c
+++ b/libcutils/sockets_unix.c
@@ -47,12 +47,25 @@
 }
 
 int socket_close(int sock) {
-  return close(sock);
+    return close(sock);
 }
 
 int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
-  struct timeval tv;
-  tv.tv_sec = timeout_ms / 1000;
-  tv.tv_usec = (timeout_ms % 1000) * 1000;
-  return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+    struct timeval tv;
+    tv.tv_sec = timeout_ms / 1000;
+    tv.tv_usec = (timeout_ms % 1000) * 1000;
+    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
+
+cutils_socket_buffer_t make_cutils_socket_buffer(void* data, size_t length) {
+    cutils_socket_buffer_t buffer;
+    buffer.iov_base = data;
+    buffer.iov_len = length;
+    return buffer;
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+                            cutils_socket_buffer_t* buffers,
+                            size_t num_buffers) {
+    return writev(sock, buffers, num_buffers);
 }
diff --git a/libcutils/sockets_windows.c b/libcutils/sockets_windows.c
index 1bf2933..8153688 100644
--- a/libcutils/sockets_windows.c
+++ b/libcutils/sockets_windows.c
@@ -58,3 +58,22 @@
     return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms,
                       sizeof(timeout_ms));
 }
+
+cutils_socket_buffer_t make_cutils_socket_buffer(void* data, size_t length) {
+    cutils_socket_buffer_t buffer;
+    buffer.buf = data;
+    buffer.len = length;
+    return buffer;
+}
+
+ssize_t socket_send_buffers(cutils_socket_t sock,
+                            cutils_socket_buffer_t* buffers,
+                            size_t num_buffers) {
+    DWORD bytes_sent = 0;
+
+    if (WSASend(sock, buffers, num_buffers, &bytes_sent, 0, NULL, NULL) !=
+            SOCKET_ERROR) {
+        return bytes_sent;
+    }
+    return -1;
+}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 6c74b9a..40fa9b1 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -34,17 +34,17 @@
     ASSERT_NE(INVALID_SOCKET, server);
     ASSERT_NE(INVALID_SOCKET, client);
 
-    char buffer[3];
+    char buffer[128];
     sockaddr_storage addr;
     socklen_t addr_size = sizeof(addr);
 
     // Send client -> server first to get the UDP client's address.
     ASSERT_EQ(3, send(client, "foo", 3, 0));
     if (type == SOCK_DGRAM) {
-        EXPECT_EQ(3, recvfrom(server, buffer, 3, 0,
+        EXPECT_EQ(3, recvfrom(server, buffer, sizeof(buffer), 0,
                               reinterpret_cast<sockaddr*>(&addr), &addr_size));
     } else {
-        EXPECT_EQ(3, recv(server, buffer, 3, 0));
+        EXPECT_EQ(3, recv(server, buffer, sizeof(buffer), 0));
     }
     EXPECT_EQ(0, memcmp(buffer, "foo", 3));
 
@@ -55,9 +55,20 @@
     } else {
         ASSERT_EQ(3, send(server, "bar", 3, 0));
     }
-    EXPECT_EQ(3, recv(client, buffer, 3, 0));
+    EXPECT_EQ(3, recv(client, buffer, sizeof(buffer), 0));
     EXPECT_EQ(0, memcmp(buffer, "bar", 3));
 
+    // Send multiple buffers using socket_send_buffers().
+    std::string data[] = {"foo", "bar", "12345"};
+    cutils_socket_buffer_t socket_buffers[3];
+    for (int i = 0; i < 3; ++i) {
+        socket_buffers[i] = make_cutils_socket_buffer(&data[i][0],
+                                                      data[i].length());
+    }
+    EXPECT_EQ(11, socket_send_buffers(client, socket_buffers, 3));
+    EXPECT_EQ(11, recv(server, buffer, sizeof(buffer), 0));
+    EXPECT_EQ(0, memcmp(buffer, "foobar12345", 11));
+
     EXPECT_EQ(0, socket_close(server));
     EXPECT_EQ(0, socket_close(client));
 }
@@ -171,3 +182,8 @@
     EXPECT_EQ(0, socket_close(client));
     EXPECT_EQ(0, socket_close(handler));
 }
+
+// Tests socket_send_buffers() failure.
+TEST(SocketsTest, TestSocketSendBuffersFailure) {
+    EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
+}