+ return off;
+}
+
+/**
+ * Copy a list of pattern items.
+ *
+ * @param[out] dst
+ * Destination buffer. Can be NULL if @p size is zero.
+ * @param size
+ * Size of @p dst in bytes.
+ * @param[in] src
+ * Source pattern items.
+ * @param num
+ * Maximum number of pattern items to process from @p src or 0 to process
+ * the entire list. In both cases, processing stops after
+ * RTE_FLOW_ITEM_TYPE_END is encountered.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * A positive value representing the number of bytes needed to store
+ * pattern items regardless of @p size on success (@p buf contents are
+ * truncated to @p size if not large enough), a negative errno value
+ * otherwise and rte_errno is set.
+ */
+static int
+rte_flow_conv_pattern(struct rte_flow_item *dst,
+ const size_t size,
+ const struct rte_flow_item *src,
+ unsigned int num,
+ struct rte_flow_error *error)
+{
+ uintptr_t data = (uintptr_t)dst;
+ size_t off;
+ size_t ret;
+ unsigned int i;
+
+ for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
+ if ((size_t)src->type >= RTE_DIM(rte_flow_desc_item) ||
+ !rte_flow_desc_item[src->type].name)
+ return rte_flow_error_set
+ (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ITEM, src,
+ "cannot convert unknown item type");
+ if (size >= off + sizeof(*dst))
+ *dst = (struct rte_flow_item){
+ .type = src->type,
+ };
+ off += sizeof(*dst);
+ if (!src->type)
+ num = i + 1;
+ }
+ num = i;
+ src -= num;
+ dst -= num;
+ do {
+ if (src->spec) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ ret = rte_flow_conv_item_spec
+ ((void *)(data + off),
+ size > off ? size - off : 0, src,
+ RTE_FLOW_CONV_ITEM_SPEC);
+ if (size && size >= off + ret)
+ dst->spec = (void *)(data + off);
+ off += ret;
+
+ }
+ if (src->last) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ ret = rte_flow_conv_item_spec
+ ((void *)(data + off),
+ size > off ? size - off : 0, src,
+ RTE_FLOW_CONV_ITEM_LAST);
+ if (size && size >= off + ret)
+ dst->last = (void *)(data + off);
+ off += ret;
+ }
+ if (src->mask) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ ret = rte_flow_conv_item_spec
+ ((void *)(data + off),
+ size > off ? size - off : 0, src,
+ RTE_FLOW_CONV_ITEM_MASK);
+ if (size && size >= off + ret)
+ dst->mask = (void *)(data + off);
+ off += ret;
+ }
+ ++src;
+ ++dst;
+ } while (--num);
+ return off;
+}
+
+/**
+ * Copy a list of actions.
+ *
+ * @param[out] dst
+ * Destination buffer. Can be NULL if @p size is zero.
+ * @param size
+ * Size of @p dst in bytes.
+ * @param[in] src
+ * Source actions.
+ * @param num
+ * Maximum number of actions to process from @p src or 0 to process the
+ * entire list. In both cases, processing stops after
+ * RTE_FLOW_ACTION_TYPE_END is encountered.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * A positive value representing the number of bytes needed to store
+ * actions regardless of @p size on success (@p buf contents are truncated
+ * to @p size if not large enough), a negative errno value otherwise and
+ * rte_errno is set.
+ */
+static int
+rte_flow_conv_actions(struct rte_flow_action *dst,
+ const size_t size,
+ const struct rte_flow_action *src,
+ unsigned int num,
+ struct rte_flow_error *error)
+{
+ uintptr_t data = (uintptr_t)dst;
+ size_t off;
+ size_t ret;
+ unsigned int i;
+
+ for (i = 0, off = 0; !num || i != num; ++i, ++src, ++dst) {
+ if ((size_t)src->type >= RTE_DIM(rte_flow_desc_action) ||
+ !rte_flow_desc_action[src->type].name)
+ return rte_flow_error_set
+ (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,
+ src, "cannot convert unknown action type");
+ if (size >= off + sizeof(*dst))
+ *dst = (struct rte_flow_action){
+ .type = src->type,
+ };
+ off += sizeof(*dst);
+ if (!src->type)
+ num = i + 1;
+ }
+ num = i;
+ src -= num;
+ dst -= num;
+ do {
+ if (src->conf) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ ret = rte_flow_conv_action_conf
+ ((void *)(data + off),
+ size > off ? size - off : 0, src);
+ if (size && size >= off + ret)
+ dst->conf = (void *)(data + off);
+ off += ret;
+ }
+ ++src;
+ ++dst;
+ } while (--num);
+ return off;
+}
+
+/**
+ * Copy flow rule components.
+ *
+ * This comprises the flow rule descriptor itself, attributes, pattern and
+ * actions list. NULL components in @p src are skipped.
+ *
+ * @param[out] dst
+ * Destination buffer. Can be NULL if @p size is zero.
+ * @param size
+ * Size of @p dst in bytes.
+ * @param[in] src
+ * Source flow rule descriptor.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * A positive value representing the number of bytes needed to store all
+ * components including the descriptor regardless of @p size on success
+ * (@p buf contents are truncated to @p size if not large enough), a
+ * negative errno value otherwise and rte_errno is set.
+ */
+static int
+rte_flow_conv_rule(struct rte_flow_conv_rule *dst,
+ const size_t size,
+ const struct rte_flow_conv_rule *src,
+ struct rte_flow_error *error)
+{
+ size_t off;
+ int ret;
+
+ rte_memcpy(dst,
+ (&(struct rte_flow_conv_rule){
+ .attr = NULL,
+ .pattern = NULL,
+ .actions = NULL,
+ }),
+ size > sizeof(*dst) ? sizeof(*dst) : size);
+ off = sizeof(*dst);
+ if (src->attr_ro) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ if (size && size >= off + sizeof(*dst->attr))
+ dst->attr = rte_memcpy
+ ((void *)((uintptr_t)dst + off),
+ src->attr_ro, sizeof(*dst->attr));
+ off += sizeof(*dst->attr);
+ }
+ if (src->pattern_ro) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ ret = rte_flow_conv_pattern((void *)((uintptr_t)dst + off),
+ size > off ? size - off : 0,
+ src->pattern_ro, 0, error);
+ if (ret < 0)
+ return ret;
+ if (size && size >= off + (size_t)ret)
+ dst->pattern = (void *)((uintptr_t)dst + off);
+ off += ret;
+ }
+ if (src->actions_ro) {
+ off = RTE_ALIGN_CEIL(off, sizeof(double));
+ ret = rte_flow_conv_actions((void *)((uintptr_t)dst + off),
+ size > off ? size - off : 0,
+ src->actions_ro, 0, error);
+ if (ret < 0)
+ return ret;
+ if (size >= off + (size_t)ret)
+ dst->actions = (void *)((uintptr_t)dst + off);
+ off += ret;
+ }
+ return off;
+}
+
+/**
+ * Retrieve the name of a pattern item/action type.
+ *
+ * @param is_action
+ * Nonzero when @p src represents an action type instead of a pattern item
+ * type.
+ * @param is_ptr
+ * Nonzero to write string address instead of contents into @p dst.
+ * @param[out] dst
+ * Destination buffer. Can be NULL if @p size is zero.
+ * @param size
+ * Size of @p dst in bytes.
+ * @param[in] src
+ * Depending on @p is_action, source pattern item or action type cast as a
+ * pointer.
+ * @param[out] error
+ * Perform verbose error reporting if not NULL.
+ *
+ * @return
+ * A positive value representing the number of bytes needed to store the
+ * name or its address regardless of @p size on success (@p buf contents
+ * are truncated to @p size if not large enough), a negative errno value
+ * otherwise and rte_errno is set.
+ */
+static int
+rte_flow_conv_name(int is_action,
+ int is_ptr,
+ char *dst,
+ const size_t size,
+ const void *src,
+ struct rte_flow_error *error)
+{
+ struct desc_info {
+ const struct rte_flow_desc_data *data;
+ size_t num;
+ };
+ static const struct desc_info info_rep[2] = {
+ { rte_flow_desc_item, RTE_DIM(rte_flow_desc_item), },
+ { rte_flow_desc_action, RTE_DIM(rte_flow_desc_action), },
+ };
+ const struct desc_info *const info = &info_rep[!!is_action];
+ unsigned int type = (uintptr_t)src;
+
+ if (type >= info->num)
+ return rte_flow_error_set
+ (error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "unknown object type to retrieve the name of");
+ if (!is_ptr)
+ return strlcpy(dst, info->data[type].name, size);
+ if (size >= sizeof(const char **))
+ *((const char **)dst) = info->data[type].name;
+ return sizeof(const char **);
+}
+
+/** Helper function to convert flow API objects. */
+int
+rte_flow_conv(enum rte_flow_conv_op op,
+ void *dst,
+ size_t size,
+ const void *src,
+ struct rte_flow_error *error)
+{
+ switch (op) {
+ const struct rte_flow_attr *attr;
+
+ case RTE_FLOW_CONV_OP_NONE:
+ return 0;
+ case RTE_FLOW_CONV_OP_ATTR:
+ attr = src;
+ if (size > sizeof(*attr))
+ size = sizeof(*attr);
+ rte_memcpy(dst, attr, size);
+ return sizeof(*attr);
+ case RTE_FLOW_CONV_OP_ITEM:
+ return rte_flow_conv_pattern(dst, size, src, 1, error);
+ case RTE_FLOW_CONV_OP_ACTION:
+ return rte_flow_conv_actions(dst, size, src, 1, error);
+ case RTE_FLOW_CONV_OP_PATTERN:
+ return rte_flow_conv_pattern(dst, size, src, 0, error);
+ case RTE_FLOW_CONV_OP_ACTIONS:
+ return rte_flow_conv_actions(dst, size, src, 0, error);
+ case RTE_FLOW_CONV_OP_RULE:
+ return rte_flow_conv_rule(dst, size, src, error);
+ case RTE_FLOW_CONV_OP_ITEM_NAME:
+ return rte_flow_conv_name(0, 0, dst, size, src, error);
+ case RTE_FLOW_CONV_OP_ACTION_NAME:
+ return rte_flow_conv_name(1, 0, dst, size, src, error);
+ case RTE_FLOW_CONV_OP_ITEM_NAME_PTR:
+ return rte_flow_conv_name(0, 1, dst, size, src, error);
+ case RTE_FLOW_CONV_OP_ACTION_NAME_PTR:
+ return rte_flow_conv_name(1, 1, dst, size, src, error);
+ }
+ return rte_flow_error_set
+ (error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "unknown object conversion operation");