commit 71d8b53e71761d15fe2cc6f06797872bcd4c9b35
parent 74f1f9366c25d65b15a435fe872cb3f9baf0810f
Author: Robert Russell <robertrussell.72001@gmail.com>
Date: Sun, 8 Jan 2023 13:31:39 -0800
Add functions for working with iovec's
Diffstat:
| M | inc/rcx/unix.h | | | 32 | ++++++++++++++++++++++++-------- |
| M | src/unix.c | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
2 files changed, 83 insertions(+), 12 deletions(-)
diff --git a/inc/rcx/unix.h b/inc/rcx/unix.h
@@ -1,15 +1,31 @@
#pragma once
+#include <sys/uio.h>
+
#include "rcx/def.h"
-/* Read exactly len bytes from fd into buf and return 0 on success. On failure,
- * return -1 and set errno in the same manner as read(2). In either case, if
- * n is not NULL, set *n to the number of bytes actually read (which is less
- * than len if and only if an error occurred). */
+/* Read exactly len bytes from fd into buf and return 0 on success. On failure
+ * (any error except EINTR), return -1 and set errno in the same manner as
+ * read(2). In either case, if n is not NULL, set *n to the number of bytes
+ * actually read (which is less than len iff an error occurred). */
int r_read_all(usize *n, int fd, void *buf, usize len);
-/* Write exactly len bytes from buf to fd and return 0 on success. On failure,
- * return -1 and set errno in the same manner as write(2). In either case, if
- * n is not NULL, set *n to the number of bytes actually written (which is less
- * than len if and only if an error occurred). */
+/* Write exactly len bytes from buf to fd and return 0 on success. On failure
+ * (any error except EINTR), return -1 and set errno in the same manner as
+ * write(2). In either case, if n is not NULL, set *n to the number of bytes
+ * actually written (which is less than len iff an error occurred). */
int r_write_all(usize *n, int fd, void *buf, usize len);
+
+/* Copy n bytes from (*src, *niov) to dst. Increment the base pointer and
+ * decrement the length in each iovec according to the number of bytes copied.
+ * If there are fewer than n bytes in (*src, *niov), behaviour is undefined. */
+void r_iovec_gather(void *dst, struct iovec **src, usize *niov, usize n);
+
+/* TODO */
+/* int r_readv_all(int fd, struct iovec **iov, usize *niov); */
+
+/* Write all bytes from (*iov, *niov) to fd and return 0 on succes. On failure
+ * (any error except EINTR), return -1 and set errno in the same manner as
+ * writev(2). Increment the base pointer and decrement the length in each iovec
+ * according to the number of bytes actually written. */
+int r_writev_all(int fd, struct iovec **iov, usize *niov);
diff --git a/src/unix.c b/src/unix.c
@@ -1,10 +1,14 @@
+#include <assert.h>
#include <errno.h>
+#include <string.h>
+#include <sys/uio.h>
#include <unistd.h>
#include "rcx/rcx.h"
typedef isize (*IoOp)(int, void *, usize);
+// TODO: handle ret == 0 and do-while -> while
static int
perform_io(usize *n, IoOp op, int fd, void *buf, usize len) {
usize i = 0;
@@ -12,15 +16,13 @@ perform_io(usize *n, IoOp op, int fd, void *buf, usize len) {
do {
isize ret = op(fd, (char *)buf + i, len - i);
if (ret < 0) {
- if (errno == EINTR)
- continue;
+ if (errno == EINTR) continue;
break;
}
i += ret;
} while (i < len);
- if (n)
- *n = i;
+ if (n) *n = i;
return i == len ? 0 : -1;
}
@@ -34,3 +36,56 @@ r_write_all(usize *n, int fd, void *buf, usize len) {
/* Cast off const-ness of buf argument from write. */
return perform_io(n, (IoOp)write, fd, buf, len);
}
+
+void
+r_iovec_gather(void *dst, struct iovec **src, usize *niov, usize n) {
+ if (n == 0) return;
+
+ for (usize i = 0;;) {
+ assert(*niov > 0);
+
+ usize l = MIN((*src)->iov_len, n);
+ memcpy((char *)dst + i, (*src)->iov_base, l);
+ (*src)->iov_base = (char *)(*src)->iov_base + l;
+ (*src)->iov_len -= l;
+ i += l;
+ n -= l;
+
+ if (n == 0) break;
+ ++*src, --*niov;
+ }
+}
+
+/* Note for future implementation of r_readv_all: Suppose readv completely
+ * fills the first k buffers, and only partially fills the kth buffer. Then
+ * we need to record the original base pointer and length of the kth buffer,
+ * slice off the partially filled part of the kth buffer, and then call
+ * readv again with all but the first k buffers. When the kth buffer eventually
+ * becomes completely filled, restore its original base pointer and length.
+ * (Of course, at this point, some other buffer now may only be partially
+ * filled and we need to repeat this logic.) */
+
+int
+r_writev_all(int fd, struct iovec **iov, usize *niov) {
+ while (*niov > 0) {
+ isize nw = writev(fd, *iov, *niov);
+ if (nw < 0) {
+ if (errno == EINTR) continue;
+ return -1;
+ }
+
+ for (; *niov > 0; ++*iov, --*niov) {
+ usize l = MIN((*iov)->iov_len, nw);
+ (*iov)->iov_base = (char *)(*iov)->iov_base + l;
+ (*iov)->iov_len -= l;
+ nw -= l;
+
+ if ((*iov)->iov_len > 0) break;
+ }
+
+ assert(nw == 0);
+ }
+
+ return 0;
+}
+