aboutgitcodebugslistschat
path: root/iov.c
diff options
context:
space:
mode:
Diffstat (limited to 'iov.c')
-rw-r--r--iov.c199
1 files changed, 187 insertions, 12 deletions
diff --git a/iov.c b/iov.c
index 3f9e229..8c74a59 100644
--- a/iov.c
+++ b/iov.c
@@ -26,7 +26,8 @@
#include "iov.h"
-/* iov_skip_bytes() - Skip leading bytes of an IO vector
+/**
+ * iov_skip_bytes() - Skip leading bytes of an IO vector
* @iov: IO vector
* @n: Number of entries in @iov
* @skip: Number of leading bytes of @iov to skip
@@ -56,8 +57,8 @@ size_t iov_skip_bytes(const struct iovec *iov, size_t n,
}
/**
- * iov_from_buf - Copy data from a buffer to an I/O vector (struct iovec)
- * efficiently.
+ * iov_from_buf() - Copy data from a buffer to an I/O vector (struct iovec)
+ * efficiently.
*
* @iov: Pointer to the array of struct iovec describing the
* scatter/gather I/O vector.
@@ -66,9 +67,8 @@ size_t iov_skip_bytes(const struct iovec *iov, size_t n,
* @buf: Pointer to the source buffer containing the data to copy.
* @bytes: Total number of bytes to copy from buf to iov.
*
- * Returns: The number of bytes successfully copied.
+ * Return: the number of bytes successfully copied.
*/
-/* cppcheck-suppress unusedFunction */
size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt,
size_t offset, const void *buf, size_t bytes)
{
@@ -97,8 +97,8 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt,
}
/**
- * iov_to_buf - Copy data from a scatter/gather I/O vector (struct iovec) to
- * a buffer efficiently.
+ * iov_to_buf() - Copy data from a scatter/gather I/O vector (struct iovec) to
+ * a buffer efficiently.
*
* @iov: Pointer to the array of struct iovec describing the scatter/gather
* I/O vector.
@@ -107,9 +107,8 @@ size_t iov_from_buf(const struct iovec *iov, size_t iov_cnt,
* @buf: Pointer to the destination buffer where data will be copied.
* @bytes: Total number of bytes to copy from iov to buf.
*
- * Returns: The number of bytes successfully copied.
+ * Return: the number of bytes successfully copied.
*/
-/* cppcheck-suppress unusedFunction */
size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt,
size_t offset, void *buf, size_t bytes)
{
@@ -127,6 +126,7 @@ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt,
/* copying data */
for (copied = 0; copied < bytes && i < iov_cnt; i++) {
size_t len = MIN(iov[i].iov_len - offset, bytes - copied);
+ ASSERT(iov[i].iov_base);
memcpy((char *)buf + copied, (char *)iov[i].iov_base + offset,
len);
copied += len;
@@ -137,14 +137,14 @@ size_t iov_to_buf(const struct iovec *iov, size_t iov_cnt,
}
/**
- * iov_size - Calculate the total size of a scatter/gather I/O vector
- * (struct iovec).
+ * iov_size() - Calculate the total size of a scatter/gather I/O vector
+ * (struct iovec).
*
* @iov: Pointer to the array of struct iovec describing the
* scatter/gather I/O vector.
* @iov_cnt: Number of elements in the iov array.
*
- * Returns: The total size in bytes.
+ * Return: the total size in bytes.
*/
size_t iov_size(const struct iovec *iov, size_t iov_cnt)
{
@@ -156,3 +156,178 @@ size_t iov_size(const struct iovec *iov, size_t iov_cnt)
return len;
}
+
+/**
+ * iov_tail_prune() - Remove any unneeded buffers from an IOV tail
+ * @tail: IO vector tail (modified)
+ *
+ * If an IOV tail's offset is large enough, it may not include any bytes from
+ * the first (or first several) buffers in the underlying IO vector. Modify the
+ * tail's representation so it contains the same logical bytes, but only
+ * includes buffers that are actually needed. This will avoid stepping through
+ * unnecessary elements of the underlying IO vector on future operations.
+ *
+ * Return: true if the tail still contains any bytes, otherwise false
+ */
+bool iov_tail_prune(struct iov_tail *tail)
+{
+ size_t i;
+
+ i = iov_skip_bytes(tail->iov, tail->cnt, tail->off, &tail->off);
+ tail->iov += i;
+ tail->cnt -= i;
+
+ return !!tail->cnt;
+}
+
+/**
+ * iov_tail_size() - Calculate the total size of an IO vector tail
+ * @tail: IO vector tail
+ *
+ * Return: the total size in bytes.
+ */
+size_t iov_tail_size(struct iov_tail *tail)
+{
+ iov_tail_prune(tail);
+ return iov_size(tail->iov, tail->cnt) - tail->off;
+}
+
+/**
+ * iov_drop_header() - Discard a header from an IOV tail
+ * @tail: IO vector tail
+ * @len: length to move the head of the tail
+ *
+ * Return: true if the item still contains any bytes, otherwise false
+ */
+bool iov_drop_header(struct iov_tail *tail, size_t len)
+{
+ tail->off = tail->off + len;
+
+ return iov_tail_prune(tail);
+}
+
+/**
+ * iov_check_header() - Check if a header can be accessed
+ * @tail: IOV tail to get header from
+ * @len: Length of header to get, in bytes
+ * @align: Required alignment of header, in bytes
+ *
+ * @tail may be pruned, but will represent the same bytes as before.
+ *
+ * Return: pointer to the first @len logical bytes of the tail, NULL if that
+ * overruns the IO vector, is not contiguous or doesn't have the
+ * requested alignment.
+ */
+static void *iov_check_header(struct iov_tail *tail, size_t len, size_t align)
+{
+ char *p;
+
+ if (!iov_tail_prune(tail))
+ return NULL; /* Nothing left */
+
+ if (tail->off + len < tail->off)
+ return NULL; /* Overflow */
+
+ if (tail->off + len > tail->iov[0].iov_len)
+ return NULL; /* Not contiguous */
+
+ p = (char *)tail->iov[0].iov_base + tail->off;
+ if ((uintptr_t)p % align)
+ return NULL; /* not aligned */
+
+ return p;
+}
+
+/**
+ * iov_peek_header_() - Get pointer to a header from an IOV tail
+ * @tail: IOV tail to get header from
+ * @v: Temporary memory to use if the memory in @tail
+ * is discontinuous
+ * @len: Length of header to get, in bytes
+ * @align: Required alignment of header, in bytes
+ *
+ * @tail may be pruned, but will represent the same bytes as before.
+ *
+ * Return: pointer to the first @len logical bytes of the tail, or to
+ * a copy if that overruns the IO vector, is not contiguous or
+ * doesn't have the requested alignment. NULL if that overruns the
+ * IO vector.
+ */
+/* cppcheck-suppress [staticFunction,unmatchedSuppression] */
+void *iov_peek_header_(struct iov_tail *tail, void *v, size_t len, size_t align)
+{
+ char *p = iov_check_header(tail, len, align);
+ size_t l;
+
+ if (p)
+ return p;
+
+ l = iov_to_buf(tail->iov, tail->cnt, tail->off, v, len);
+ if (l != len)
+ return NULL;
+
+ return v;
+}
+
+/**
+ * iov_remove_header_() - Remove a header from an IOV tail
+ * @tail: IOV tail to remove header from (modified)
+ * @v: Temporary memory to use if the memory in @tail
+ * is discontinuous
+ * @len: Length of header to remove, in bytes
+ * @align: Required alignment of header, in bytes
+ *
+ * On success, @tail is updated so that it longer includes the bytes of the
+ * returned header.
+ *
+ * Return: pointer to the first @len logical bytes of the tail, or to
+ * a copy if that overruns the IO vector, is not contiguous or
+ * doesn't have the requested alignment. NULL if that overruns the
+ * IO vector.
+ */
+void *iov_remove_header_(struct iov_tail *tail, void *v, size_t len, size_t align)
+{
+ char *p = iov_peek_header_(tail, v, len, align);
+
+ if (!p)
+ return NULL;
+
+ tail->off = tail->off + len;
+
+ return p;
+}
+
+/**
+ * iov_tail_clone() - Clone an iov tail into a new iovec array
+ *
+ * @dst_iov: Pointer to the destination array of struct iovec describing
+ * the scatter/gather I/O vector to shallow copy to.
+ * @dst_iov_cnt: Maximum number of elements in the destination iov array.
+ * @tail: Pointer to the source iov_tail
+ *
+ * Return: the number of elements successfully referenced from the destination
+ * iov array, a negative value if there is not enough room in the
+ * destination iov array
+ */
+ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt,
+ struct iov_tail *tail)
+{
+ const struct iovec *iov = &tail->iov[0];
+ size_t iov_cnt = tail->cnt;
+ size_t offset = tail->off;
+ unsigned int i, j;
+
+ i = iov_skip_bytes(iov, iov_cnt, offset, &offset);
+
+ /* assign iov references referencing a subset of the source one */
+ for (j = 0; i < iov_cnt && j < dst_iov_cnt; i++, j++) {
+ dst_iov[j].iov_base = (char *)iov[i].iov_base + offset;
+ dst_iov[j].iov_len = iov[i].iov_len - offset;
+ offset = 0;
+ }
+
+ if (j == dst_iov_cnt && i != iov_cnt)
+ return -1;
+
+ return j;
+}