From cbbefb09078331b98d523f24a0fa87be5db81d5e Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sun, 20 Apr 2025 08:21:00 -0600 Subject: libhw_generic: io.h: Tidy up --- libhw_generic/include/libhw/generic/io.h | 67 ++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 17 deletions(-) (limited to 'libhw_generic/include/libhw/generic/io.h') diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h index ebbd6cb..19edbe6 100644 --- a/libhw_generic/include/libhw/generic/io.h +++ b/libhw_generic/include/libhw/generic/io.h @@ -15,6 +15,8 @@ /* structs ********************************************************************/ +/* iovec ===========================================================*/ + #if __unix__ #include #else @@ -23,6 +25,11 @@ struct iovec { size_t iov_len; }; #endif +typedef struct iovec iovec; +DECLARE_ERROR_OR(iovec); +DECLARE_ERROR_AND(iovec); + +/* duplex_iovec ====================================================*/ #define IOVEC_DISCARD ((void*)(~((uintptr_t)0))) @@ -36,8 +43,11 @@ struct duplex_iovec { void *iov_write_from; size_t iov_len; }; +typedef struct duplex_iovec duplex_iovec; +DECLARE_ERROR_OR(duplex_iovec); +DECLARE_ERROR_AND(duplex_iovec); -/* utilities ******************************************************************/ +/* iovec utilities ************************************************************/ /* If byte_max_cnt == 0, then there is no maximum. */ @@ -57,9 +67,25 @@ void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, in /* basic interfaces ***********************************************************/ +/* + * Conventions: + * + * - Naming: + * + The "v"[ec] suffix means a sequence of iovecs, instead of a + * simple buf+len. + * + * - Errors: + * + Short *reads* are *not* errors (and so return size_t *or* + * error). + * + Short *writes* *are* errors (and so return size_t *and* + * (possibly-null) error). + */ + +/* read ============================================================*/ + /** * Return bytes-read on success. A short read is *not* an error - * (unlike writev). + * (unlike `write` methods). * * It is invalid to call readv when the sum length of iovecs is 0. */ @@ -69,8 +95,11 @@ LO_INTERFACE(io_reader); #define io_readv(r, iov, iovcnt) LO_CALL(r, readv, iov, iovcnt) #define io_read(r, buf, count) LO_CALL(r, readv, &((struct iovec){.iov_base = buf, .iov_len = count}), 1) +/* write ===========================================================*/ + /** - * Return bytes-written. A short write *is* an error (unlike readv) + * Return bytes-written. A short write *is* an error (unlike `read` + * methods). * * Writes are *not* guaranteed to be atomic, so if you have concurrent * writers then you should arrange for a mutex to protect the writer. @@ -83,21 +112,11 @@ LO_INTERFACE(io_writer); #define io_writev(w, iov, iovcnt) LO_CALL(w, writev, iov, iovcnt) #define io_write(w, buf, count) LO_CALL(w, writev, &((struct iovec){.iov_base = buf, .iov_len = count}), 1) -#define io_closer_LO_IFACE \ - LO_FUNC(error, close) -LO_INTERFACE(io_closer); -#define io_close(c) LO_CALL(c, close) - -#define io_bidi_closer_LO_IFACE \ - LO_NEST(io_closer) \ - LO_FUNC(error, close_read) \ - LO_FUNC(error, close_write) -LO_INTERFACE(io_bidi_closer); -#define io_close_read(c) LO_CALL(c, close_read) -#define io_close_write(c) LO_CALL(c, close_write) +/* readwrite =======================================================*/ /** - * A short read/write *is* an error (like writev and unlike readv). + * A short read/write *is* an error (like `write` methods and unlike + * `read` methods). * * Reads/writes are *not* guaranteed to be atomic, so if you have * concurrent readers/writers then you should arrange for a mutex to @@ -108,10 +127,24 @@ LO_INTERFACE(io_bidi_closer); #define io_duplex_readwriter_LO_IFACE \ LO_FUNC(size_t_and_error, readwritev, const struct duplex_iovec *iov, int iovcnt) LO_INTERFACE(io_duplex_readwriter); - #define io_readwritev(rw, iov, iovcnt) \ LO_CALL(rw, readwritev, iov, iovcnt) +/* close ===========================================================*/ + +#define io_closer_LO_IFACE \ + LO_FUNC(error, close) +LO_INTERFACE(io_closer); +#define io_close(c) LO_CALL(c, close) + +#define io_bidi_closer_LO_IFACE \ + LO_NEST(io_closer) \ + LO_FUNC(error, close_read) \ + LO_FUNC(error, close_write) +LO_INTERFACE(io_bidi_closer); +#define io_close_read(c) LO_CALL(c, close_read) +#define io_close_write(c) LO_CALL(c, close_write) + /* aggregate interfaces *******************************************************/ #define io_readwriter_LO_IFACE \ -- cgit v1.2.3-2-g168b From 4109b15224b08fe49297097d9891e4c584712400 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Sun, 20 Apr 2025 08:21:00 -0600 Subject: libhw_generic: io.h: Define some new interfaces --- libhw_generic/include/libhw/generic/io.h | 69 +++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) (limited to 'libhw_generic/include/libhw/generic/io.h') diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h index 19edbe6..c615932 100644 --- a/libhw_generic/include/libhw/generic/io.h +++ b/libhw_generic/include/libhw/generic/io.h @@ -7,14 +7,21 @@ #ifndef _LIBHW_GENERIC_IO_H_ #define _LIBHW_GENERIC_IO_H_ -#include /* for size_t */ -#include /* for uintptr_t */ +#include /* for size_t */ +#include /* for uintptr_t, uint{n}_t, UINT{n}_MAX */ #include #include /* structs ********************************************************************/ +/* uoff_t ==========================================================*/ + +typedef uint64_t uoff_t; +#define UOFF_MAX UINT64_MAX +DECLARE_ERROR_OR(uoff_t); +DECLARE_ERROR_AND(uoff_t); + /* iovec ===========================================================*/ #if __unix__ @@ -71,6 +78,8 @@ void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, in * Conventions: * * - Naming: + * + The "p"[osition] prefix means an explicit `uoff_t offset`, + * instead of using an internal cursor. * + The "v"[ec] suffix means a sequence of iovecs, instead of a * simple buf+len. * @@ -95,6 +104,18 @@ LO_INTERFACE(io_reader); #define io_readv(r, iov, iovcnt) LO_CALL(r, readv, iov, iovcnt) #define io_read(r, buf, count) LO_CALL(r, readv, &((struct iovec){.iov_base = buf, .iov_len = count}), 1) +/** + * Returns bytes-read on success. A short read is *not* an error + * (unlike `write` methods). + * + * It is invalid to call preadv when the sum length of iovecs is 0. + */ +#define io_preader_LO_IFACE \ + LO_FUNC(size_t_or_error, preadv, const struct iovec *iov, int iovcnt, uoff_t offset) +LO_INTERFACE(io_preader); +#define io_preadv(r, iov, iovcnt, off) LO_CALL(r, preadv, iov, iovcnt, off) +#define io_pread(r, buf, count, off) LO_CALL(r, preadv, &((struct iovec){.iov_base = buf, .iov_len = count}), 1, off) + /* write ===========================================================*/ /** @@ -112,6 +133,21 @@ LO_INTERFACE(io_writer); #define io_writev(w, iov, iovcnt) LO_CALL(w, writev, iov, iovcnt) #define io_write(w, buf, count) LO_CALL(w, writev, &((struct iovec){.iov_base = buf, .iov_len = count}), 1) +/** + * Returns bytes-written. A short write *is* an error (unlike `read` + * methods). + * + * Writes are *not* guaranteed to be atomic, so if you have concurrent + * writers then you should arrange for a mutex to protect the writer. + * + * It is invalid to call writev when the sum length of iovecs is 0. + */ +#define io_pwriter_LO_IFACE \ + LO_FUNC(size_t_and_error, pwritev, const struct iovec *iov, int iovcnt, uoff_t offset) +LO_INTERFACE(io_pwriter); +#define io_pwritev(r, iov, iovcnt, off) LO_CALL(r, pwritev, iov, iovcnt, off) +#define io_pwrite(r, buf, count, off) LO_CALL(r, pwritev, &((struct iovec){.iov_base = buf, .iov_len = count}), 1, off) + /* readwrite =======================================================*/ /** @@ -130,6 +166,35 @@ LO_INTERFACE(io_duplex_readwriter); #define io_readwritev(rw, iov, iovcnt) \ LO_CALL(rw, readwritev, iov, iovcnt) +/* read then write =================================================*/ + +/** + * LO_CALL(src, pread_to, dst, src_offset, count) is functionally: + * + * size_t_and_error copy(lo_interface io_writer dst, lo_interface io_preader src, uoff_t src_offset, size_t count) { + * buf = malloc(count); + * size_t_or_error read_r = io_pread(src, buf, count, src_offset); + * if (read_r.is_err) + * return ERROR_AND(size_t, 0, read_r.err); + * size_t_and_error write_r = io_write(dst, buf, read_r.size_t); + * free(buf); + * return write_r; + * } + * + * except that it does not need to allocate a buffer. That is: It + * allows the reader to dump its data directly to the writer. This + * allows us to save a copy when the reader already has an in-memory + * buffer containing the data. + * + * The above code defines when a short read/write is an error or not. + * + * It must call writev() exactly 0-or-1 times, and if it does call it, + * then it must be called with exactly 1 iovec. + */ +#define io_preader_to_LO_IFACE \ + LO_FUNC(size_t_and_error, pread_to, lo_interface io_writer dst, uoff_t src_offset, size_t count) +LO_INTERFACE(io_preader_to); + /* close ===========================================================*/ #define io_closer_LO_IFACE \ -- cgit v1.2.3-2-g168b