On Wed, 20 Nov 2019 13:53:08 +0100 Phil Sutter <phil@xxxxxx> wrote: > On Wed, Nov 20, 2019 at 12:49:54PM +0100, Stefano Brivio wrote: > > > On Tue, 19 Nov 2019 23:12:38 +0100 > > Phil Sutter <phil@xxxxxx> wrote: > [...] > > > So, the whole thing would look like (together with the other change > > you suggest): > > > > -- > > static int netlink_gen_concat_data_expr(const struct expr *i, > > unsigned char *data) > > { > > if (i->etype == EXPR_RANGE) { > > const struct expr *e; > > > > if (i->flags & EXPR_F_INTERVAL_END) > > e = i->right; > > else > > e = i->left; > > > > return netlink_export_pad(data, e->value, e); > > } > > > > if (i->etype == EXPR_PREFIX) { > > if (i->flags & EXPR_F_INTERVAL_END) { > > int count; > > mpz_t v; > > > > mpz_init_bitmask(v, i->len - i->prefix_len); > > mpz_add(v, i->prefix->value, v); > > count = netlink_export_pad(data, v, i); > > mpz_clear(v); > > return count; > > } > > > > return netlink_export_pad(data, i->prefix->value, i); > > } > > > > assert(i->etype == EXPR_VALUE); > > > > return netlink_export_pad(data, i->value, i); > > } > > I would even: > > | static int > | netlink_gen_concat_data_expr(const struct expr *i, unsigned char *data) > | { > | mpz_t *valp = NULL; > | > | switch (i->etype) { > | case EXPR_RANGE: > | i = (i->flags & EXPR_F_INTERVAL_END) ? i->right : i->left; > | break; > | case EXPR_PREFIX: > | if (i->flags & EXPR_F_INTERVAL_END) { > | int count; > | mpz_t v; > | > | mpz_init_bitmask(v, i->len - i->prefix_len); > | mpz_add(v, i->prefix->value, v); > | count = netlink_export_pad(data, v, i); > | mpz_clear(v); > | return count; > | } > | valp = &i->prefix->value; > | break; > | case EXPR_VALUE: > | break; > | default: > | BUG("invalid expression type '%s' in set", expr_ops(i)->name); > | } > | > | return netlink_export_pad(data, valp ? *valp : i->value, i); > | } > > But that's up to you. :) I think it's nicer with a switch and that BUG() is more helpful than a random assert, but I personally find that ternary condition on valp a bit difficult to follow. I'd recycle most of your function without that -- it's actually shorter and still makes it clear enough what gets fed to netlink_export_pad(). Oh, and by the way, at this point we need to check flags on the 'key' expression. > > static void netlink_gen_concat_data(const struct expr *expr, > > struct nft_data_linearize *nld) > > { > > unsigned int len = expr->len / BITS_PER_BYTE, offset = 0; > > unsigned char data[len]; > > const struct expr *i; > > > > memset(data, 0, len); > > > > list_for_each_entry(i, &expr->expressions, list) > > offset += netlink_gen_concat_data_expr(i, data + offset); > > > > memcpy(nld->value, data, len); > > nld->len = len; > > } > > -- > > > > Is that better? > > Looks great, thanks! > > [...] > > > > So this is the fifth copy of the same piece of code. :( > > > > > > Isn't there a better way to solve that? > > > > I couldn't think of any. > > I guess we would need an intermediate state which is 'multiton_rhs_expr > DOT multiton_rhs_expr'. Might turn into a mess as well. :) I just tried to add that, and kept losing myself in the middle of it. It might be me, but it doesn't sound that promising when it comes to readability later. I guess we already have enough levels of indirection here -- I'd just go with the compound_expr_alloc_or_add() you suggested. > > > Intuitively, I would just: > > > > > > | int tmp = len; > > > | while (start != end && !(start & 1) && (end & 1) && tmp) { > > > | start >>= 1; > > > | end >>= 1; > > > | tmp--; > > > | } > > > | return (tmp && start == end) ? len - tmp : -1; > > > > > > Is that slow when dealing with gmp? > > > > I don't think so, it also avoid copies and allocations, while shifting > > and setting bits have comparable complexities. I'd go with the gmp > > version of this. Actually, I need to preserve the original elements, so the two copies are needed anyway -- other than that, I basically recycled your function. -- Stefano