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.
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.
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.
When the function is inlined, the non-matching cases turn to dead code since the switch expression is constant.