On Tue, Apr 15, 2025 at 11:42:42AM +0200, Laurent Vivier wrote: > iov_tail_drop() discards a header from the iov_tail, > > iov_slice() makes a new iov referencing a subset of the data > in the original iov. > > iov_tail_slice() is a wrapper for iov_slice() using iov_tail > > Signed-off-by: Laurent Vivier > --- > iov.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > iov.h | 6 ++++ > 2 files changed, 98 insertions(+) > > diff --git a/iov.c b/iov.c > index 8c63b7ea6e31..60ab1f9af973 100644 > --- a/iov.c > +++ b/iov.c > @@ -156,6 +156,59 @@ size_t iov_size(const struct iovec *iov, size_t iov_cnt) > return len; > } > > +/** > + * iov_slice - Make a new iov referencing a subset of the data in the original iov > + * > + * @dst_iov: Pointer to the destination array of struct iovec describing > + * the scatter/gather I/O vector to copy to. The indentation seems a bit wonky here, looks like some lines are using spaces and others tabs. > + * @dst_iov_cnt: Maximum number of elements in the destination iov array. > + * @iov: Pointer to the source array of struct iovec describing > + * the scatter/gather I/O vector to copy from. > + * @iov_cnt: Maximum number of elements in the source iov array. > + * @offset: Offset within the source iov from where copying should start. > + * @bytes: On input, total number of bytes to copy from @iov to @dst_iov, > + * if @bytes is NULL, copy all the content of @iov, > + * on output actual number of bytes copied. > + * > + * Returns: The number of elements successfully copied to the destination > + * iov array, a negative value if there is not enough room in the > + * destination iov array > + */ > +/* cppcheck-suppress staticFunction */ > +int iov_slice(struct iovec *dst_iov, size_t dst_iov_cnt, > + const struct iovec *iov, size_t iov_cnt, > + size_t offset, size_t *bytes) The return value, is (usually) an iov count. Since that's given as a size_t in most places, it probably makes sense to use an ssize_t in the return value, rather than plain int. > +{ > + unsigned int i, j; > + size_t remaining = bytes ? *bytes : SIZE_MAX; > + > + i = iov_skip_bytes(iov, iov_cnt, offset, &offset); > + > + /* create a new iov array referencing a subset of the source one */ > + for (j = 0; i < iov_cnt && j < dst_iov_cnt && remaining; i++) { > + size_t len = MIN(remaining, iov[i].iov_len - offset); > + > + dst_iov[j].iov_base = (char *)iov[i].iov_base + offset; > + dst_iov[j].iov_len = len; > + j++; > + remaining -= len; > + offset = 0; > + } > + if (j == dst_iov_cnt) { > + if (bytes) { > + if (remaining) > + return -1; > + } else if (i != iov_cnt) { > + return -1; > + } > + } > + > + if (bytes) > + *bytes -= remaining; > + > + return j; > +} > + > /** > * iov_tail_prune() - Remove any unneeded buffers from an IOV tail > * @tail: IO vector tail (modified) > @@ -191,6 +244,21 @@ size_t iov_tail_size(struct iov_tail *tail) > return iov_size(tail->iov, tail->cnt) - tail->off; > } > > +/** > + * iov_tail_drop() - Discard a header from an IOV tail > + * @tail: IO vector tail > + * @len: length to move the head of the tail > + * > + * Returns: true if the tail still contains any bytes, otherwise false > + */ > +/* cppcheck-suppress unusedFunction */ > +bool iov_tail_drop(struct iov_tail *tail, size_t len) > +{ > + tail->off = tail->off + len; > + > + return iov_tail_prune(tail); > +} > + > /** > * iov_peek_header_() - Get pointer to a header from an IOV tail > * @tail: IOV tail to get header from > @@ -247,3 +315,27 @@ void *iov_remove_header_(struct iov_tail *tail, size_t len, size_t align) > tail->off = tail->off + len; > return p; > } > + > +/** > + * iov_tail_slice - Make a new iov referencing a subset of the data > + * in an iov_tail > + * > + * @dst_iov: Pointer to the destination array of struct iovec describing > + * the scatter/gather I/O vector to copy to. > + * @dst_iov_cnt: Maximum number of elements in the destination iov array. > + * @tail: Pointer to the source iov_tail > + * @bytes: On input, total number of bytes to copy from @iov to @dst_iov, > + * if @bytes is NULL, copy all the content of @iov, > + * on output actual number of bytes copied. Do you have any use cases that need this parameter? The ones I remember from v2 all wanted to copy the entire remainder of the iov_tail. > + * Returns: The number of elements successfully copied to the destination > + * iov array, a negative value if there is not enough room in the > + * destination iov array > + */ > +/* cppcheck-suppress unusedFunction */ > +int iov_tail_slice(struct iovec *dst_iov, size_t dst_iov_cnt, > + struct iov_tail *tail, size_t *bytes) > +{ > + return iov_slice(dst_iov, dst_iov_cnt, &tail->iov[0], tail->cnt, > + tail->off, bytes); > +} > diff --git a/iov.h b/iov.h > index 9855bf0c0c32..4bbdbf23f27b 100644 > --- a/iov.h > +++ b/iov.h > @@ -28,6 +28,9 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt, > size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt, > size_t offset, void *buf, size_t bytes); > size_t iov_size(const struct iovec *iov, size_t iov_cnt); > +int iov_slice(struct iovec *dst_iov, size_t dst_iov_cnt, > + const struct iovec *iov, size_t iov_cnt, > + size_t offset, size_t *bytes); > > /* > * DOC: Theory of Operation, struct iov_tail > @@ -72,8 +75,11 @@ struct iov_tail { > > bool iov_tail_prune(struct iov_tail *tail); > size_t iov_tail_size(struct iov_tail *tail); > +bool iov_tail_drop(struct iov_tail *tail, size_t len); > void *iov_peek_header_(struct iov_tail *tail, size_t len, size_t align); > void *iov_remove_header_(struct iov_tail *tail, size_t len, size_t align); > +int iov_tail_slice(struct iovec *dst_iov, size_t dst_iov_cnt, > + struct iov_tail *tail, size_t *bytes); > > /** > * IOV_PEEK_HEADER() - Get typed pointer to a header from an IOV tail -- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson