rcx

library of miscellaneous bits of C code
git clone git://git.rr3.xyz/rcx
Log | Files | Refs | README | LICENSE

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:
Minc/rcx/unix.h | 32++++++++++++++++++++++++--------
Msrc/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; +} +