tech-kern archive

# proposal: some pointer and arithmetic utilities

```I'd like to add/change the following utilities for pointers and
arithmetic.  Here is the summary of definitions I'm proposing;
detailed rationale follows below.  Comments?

#define roundup2(x, m)  (((x) + ((m) - 1)) & ~((m) - 1 + ((x) - (x))))
#define rounddown2(x,m) ((x) & ~((m) - 1 + ((x) + (x))))
#define offsetin(s, f)  (((const char *)&(s)->f) - ((const char *)&(s)))
#define container_of(p, t, f)                                           \
((void)sizeof((p) -                                             \
&((t *)(((char *)(p)) - offsetof(t, f)))->f),           \
((t *)(((char *)(p)) - offsetof(t, f))))

** fix roundup2 / add rounddown2

Given

uint64_t x = 0x0123456789abcdef;
unsigned int n = 64;

roundup2(x, n) as defined currently returns 0x0000000089abcdc0, not
0x0123456789abcdc0, but if you change unsigned int to anything else,
it gives 0x0123456789abcdc0 as anyone sensible would expect.

If we change the definition to

#define roundup2(x, m)  (((x) + ((m) - 1)) & ~((m) - 1 + ((x) - (x))))

then the extra addition of zero forces conversion of m - 1 to the
width of x before taking the complement, which -- if computed as
unsigned int -- would be zero-extended just before the AND, having the
absurd zeroing effect.

I'd also like to add

#define rounddown2(x,m) ((x) & ~((m) - 1 + ((x) + (x))))

so that we can name some of the ad hoc uses of this idiom and make it
easier to avoid the integer conversion botch.

If we had typeof, we could avoid multiple evaluation of x and m too:

#define roundup2(x, m)  ((((x) - 1) | ((m) - 1)) + 1)
#define rounddown2(x,m) ((x) & ~((typeof(x))((m) - 1)))

(These definitions are more or less what Linux uses.)

Otherwise, I'd also like to add a stern warning in the man page for
these that they are macros that may multiply evaluate their operands.

We use

struct foo *x = alloc(sizeof(*x));

struct foo *x = alloc(sizeof(struct foo));

so that if we change the type of x we need not remember to change the
sizeof -- the compiler will do it for us.  But if struct foo has a
variable-length array member for which we want to allocate space for n
elements, the idiom we use is

struct foo *x = alloc(offsetof(struct foo, f_data[n]));

which has the same problem as sizeof(struct foo).

I'd like to add

#define offsetin(s, f)  (((const char *)&(s)->f) - ((const char *)&(s)))

so that we can write

struct foo *x = alloc(offsetin(*x, f_data[n]));

with the same advantages as sizeof(*x).

We have various structs of the form

struct foo {
struct bar f_bar;
...
};

and routines that are called like mumble(&foo->f_bar) and look like

int
mumble(struct bar *bar)
{
struct foo *foo = (void *)bar;
...
}

which assume that nobody has rearranged the fields of struct foo, an
assumption which is neither semantically significant nor checked by
the compiler; nor does it work with multiple different members from
whose pointers we may want to find the containing object.

I'd like to add

#define container_of(p, t, f)                                           \
((void)sizeof((p) -                                             \
&((t *)(((char *)(p)) - offsetof(t, f)))->f),           \
((t *)(((char *)(p)) - offsetof(t, f))))

so that foo = container_of(&foo->f_bar, struct foo, f_bar), which lets
us write

struct foo *foo = container_of(bar, struct foo, f_bar);

without making the above assumption.  The sizeof is a trick to get the
compiler to check that bar points to the same type as the member f_bar
of struct foo; without this check it would just be

#define container_of(p, t, f)   ((t *)(((char *)(p)) - offsetof(t, f)))

(This idiom came from Linux, although Linux uses typeof and the GCC
expression block extension instead of sizeof for type safety.)
```

Home | Main Index | Thread Index | Old Index