export netbsd's reallocarr proposal. (#433)

* export netbsd's reallocarr proposal.

acts subtly differently from reallocarray, returns an error code
and first argument as receiver.

* not export by default

* ci tests

* apply suggestions

* doc addition

* Apply suggestions from code review

Co-authored-by: Matthew Parkinson <mjp41@users.noreply.github.com>
This commit is contained in:
David CARLIER
2021-12-02 14:49:32 +00:00
committed by GitHub
parent 7f3642e05c
commit 360efa2123
4 changed files with 164 additions and 1 deletions

View File

@@ -101,7 +101,7 @@ extern "C"
return p;
}
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
#if !defined(SNMALLOC_NO_REALLOCARRAY)
SNMALLOC_EXPORT void*
SNMALLOC_NAME_MANGLE(reallocarray)(void* ptr, size_t nmemb, size_t size)
{
@@ -116,6 +116,45 @@ extern "C"
}
#endif
#if !defined(SNMALLOC_NO_REALLOCARR)
SNMALLOC_EXPORT int
SNMALLOC_NAME_MANGLE(reallocarr)(void* ptr_, size_t nmemb, size_t size)
{
int err = errno;
auto& a = ThreadAlloc::get();
bool overflow = false;
size_t sz = bits::umul(size, nmemb, overflow);
if (sz == 0)
{
errno = err;
return 0;
}
if (overflow)
{
errno = err;
return EOVERFLOW;
}
void** ptr = reinterpret_cast<void**>(ptr_);
void* p = a.alloc(sz);
if (p == nullptr)
{
errno = ENOMEM;
return ENOMEM;
}
sz = bits::min(sz, a.alloc_size(*ptr));
// Guard memcpy as GCC is assuming not nullptr for ptr after the memcpy
// otherwise.
if (sz != 0)
memcpy(p, *ptr, sz);
errno = err;
a.dealloc(*ptr);
*ptr = p;
return 0;
}
#endif
SNMALLOC_EXPORT void*
SNMALLOC_NAME_MANGLE(memalign)(size_t alignment, size_t size)
{

View File

@@ -2,6 +2,8 @@
#include <test/setup.h>
#define SNMALLOC_NAME_MANGLE(a) our_##a
#undef SNMALLOC_NO_REALLOCARRAY
#undef SNMALLOC_NO_REALLOCARR
#include "../../../override/malloc.cc"
using namespace snmalloc;
@@ -138,6 +140,60 @@ void test_memalign(size_t size, size_t align, int err, bool null)
check_result(size, align, p, err, null);
}
void test_reallocarray(void* p, size_t nmemb, size_t size, int err, bool null)
{
size_t old_size = 0;
size_t tsize = nmemb * size;
if (p != nullptr)
old_size = our_malloc_usable_size(p);
printf("reallocarray(%p(%zu), %zu)\n", p, old_size, tsize);
errno = SUCCESS;
auto new_p = our_reallocarray(p, nmemb, size);
if (new_p == nullptr && tsize != 0)
our_free(p);
check_result(tsize, 1, new_p, err, null);
}
void test_reallocarr(
size_t size_old, size_t nmemb, size_t size, int err, bool null)
{
void* p = nullptr;
if (size_old != (size_t)~0)
p = our_malloc(size_old);
errno = SUCCESS;
int r = our_reallocarr(&p, nmemb, size);
if (r != err)
{
printf("reallocarr failed! expected %d got %d\n", err, r);
abort();
}
printf("reallocarr(%p(%zu), %zu)\n", p, nmemb, size);
check_result(nmemb * size, 1, p, err, null);
p = our_malloc(size);
if (!p)
{
return;
}
for (size_t i = 1; i < size; i++)
static_cast<char*>(p)[i] = 1;
our_reallocarr(&p, nmemb, size);
if (r != SUCCESS)
our_free(p);
for (size_t i = 1; i < size; i++)
{
if (static_cast<char*>(p)[i] != 1)
{
printf("data consistency failed! at %zu", i);
abort();
}
}
our_free(p);
}
int main(int argc, char** argv)
{
UNUSED(argc);
@@ -229,6 +285,53 @@ int main(int argc, char** argv)
test_posix_memalign(0, align + 1, EINVAL, true);
}
test_reallocarray(nullptr, 1, 0, SUCCESS, false);
for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)
{
const size_t size = bits::one_at_bit(sc);
test_reallocarray(our_malloc(size), 1, size, SUCCESS, false);
test_reallocarray(our_malloc(size), 1, 0, SUCCESS, false);
test_reallocarray(nullptr, 1, size, SUCCESS, false);
test_reallocarray(our_malloc(size), 1, ((size_t)-1) / 2, ENOMEM, true);
for (smallsizeclass_t sc2 = 0; sc2 < (MAX_SMALL_SIZECLASS_BITS + 4); sc2++)
{
const size_t size2 = bits::one_at_bit(sc2);
test_reallocarray(our_malloc(size), 1, size2, SUCCESS, false);
test_reallocarray(our_malloc(size + 1), 1, size2, SUCCESS, false);
}
}
test_reallocarr((size_t)~0, 1, 0, SUCCESS, false);
test_reallocarr((size_t)~0, 1, 16, SUCCESS, false);
for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)
{
const size_t size = bits::one_at_bit(sc);
test_reallocarr(size, 1, size, SUCCESS, false);
test_reallocarr(size, 1, 0, SUCCESS, false);
test_reallocarr(size, 2, size, SUCCESS, false);
void* p = our_malloc(size);
if (p == nullptr)
{
printf("realloc alloc failed with %zu\n", size);
abort();
}
int r = our_reallocarr(&p, 1, ((size_t)-1) / 2);
if (r != ENOMEM)
{
printf("expected failure on allocation\n");
abort();
}
our_free(p);
for (smallsizeclass_t sc2 = 0; sc2 < (MAX_SMALL_SIZECLASS_BITS + 4); sc2++)
{
const size_t size2 = bits::one_at_bit(sc2);
printf("size1: %zu, size2:%zu\n", size, size2);
test_reallocarr(size, 1, size2, SUCCESS, false);
}
}
if (our_malloc_usable_size(nullptr) != 0)
{
printf("malloc_usable_size(nullptr) should be zero");