Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The multiplication factor is often some small constant, like 2 in the case of an array grown by doubling. It's worth making reallocarray an inline function to handle a few constant values:

    inline void *reallocarray(void *old, size_t x, size_t y)
    {
       switch (y) {
       case 1:
         return realloc(old, x);
         ...
       case 2:
         // drastically simplified overflow check
         ...

       default:
         // perhaps a switch (x) with mirror cases here
         return realloc_array_general(old, x, y);
       }
    }
The 1 case occurs when programmers are following some coding convention whereby malloc calls are replaced with this "single allocator function to bind them all": char *buf = reallocarray(size, 1);

When the function is inlined, the non-matching cases turn to dead code since the switch expression is constant.



I just looked at some of my recent code where I'm using realloc. It's obvious that this approach is insufficient, because it assumes there is only one multiplication. I have situations where an array is being resized. The size is expressed in terms of elements, rather than bytes. So two multiplications are going on: the size is increased first, and then in the realloc call, the argument expression has a multiplication by the element size also.

An overflow checking realloc won't help if the first multiplication oveflows.

If I make this version instead:

    void *realloc_array(void *old,
                        size_t existing_size,
                        size_t resize_factor,
                        size_t elsize);
that's no good either because I want the new size also if everything went well. I need something like:

    void *realloc_array(void *old,
                        size_t existing_size,
                        size_t resize_factor,
                        size_t elsize,
                        size_t *p_newsize_out);
Okay, now we're talking. Resize my vector old, which contains existing_size elements, by a factor of resize_factor. The individual elements have size elsize. If all goes well (non-NULL is returned), give me the new size (in elements, not bytes) in the location *p_newsize_out.


After some thought and experimentation, I have arraived at this interface:

    void *resize_array(void *old,
                       size_t existing_size,
                       size_t new_size
                       size_t resize_factor,
                       size_t elsize);
The client code computes the new size, while retaining the old. If these are supposed to be related by an integer factor, then this is passed as resize_factor. If they are not related that way, then resize_factor is passed in as zero. The elsize parameter must be nonzero, in any case.

If a nonzero resize_factor is supplied, then the calculation new_size = existing_size * resize_factor is checked for overflow, otherwise the zero value of resize_factor indicates that this check is not applicable; however, the function still at least validates that new_size > existing_size.

The calculation bytes = new_size * elsize is always checked for overflow.


This would slow down the general case where the size is variable, but you could combine it with __builtin_constant_p in GCC.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: