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

@@ -16,6 +16,8 @@ option(USE_SNMALLOC_STATS "Track allocation stats" OFF)
option(SNMALLOC_CI_BUILD "Disable features not sensible for CI" OFF)
option(SNMALLOC_QEMU_WORKAROUND "Disable using madvise(DONT_NEED) to zero memory on Linux" Off)
option(SNMALLOC_USE_CXX17 "Build as C++17 for legacy support." OFF)
option(SNMALLOC_NO_REALLOCARRAY "Build without reallocarray exported" ON)
option(SNMALLOC_NO_REALLOCARR "Build without reallocarr exported" ON)
# Options that apply only if we're not building the header-only library
cmake_dependent_option(SNMALLOC_RUST_SUPPORT "Build static library for rust" OFF "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF)
cmake_dependent_option(SNMALLOC_STATIC_LIBRARY "Build static libraries" ON "NOT SNMALLOC_HEADER_ONLY_LIBRARY" OFF)
@@ -195,6 +197,12 @@ add_as_define(USE_SNMALLOC_STATS)
add_as_define(SNMALLOC_QEMU_WORKAROUND)
add_as_define(SNMALLOC_CI_BUILD)
add_as_define(SNMALLOC_PLATFORM_HAS_GETENTROPY)
if (SNMALLOC_NO_REALLOCARRAY)
add_as_define(SNMALLOC_NO_REALLOCARRAY)
endif()
if (SNMALLOC_NO_REALLOCARR)
add_as_define(SNMALLOC_NO_REALLOCARR)
endif()
target_compile_definitions(snmalloc INTERFACE $<$<BOOL:CONST_QUALIFIED_MALLOC_USABLE_SIZE>:MALLOC_USABLE_SIZE_QUALIFIER=const>)

View File

@@ -113,3 +113,16 @@ You will also need to compile the relevant parts of snmalloc itself. Create a ne
#include "snmalloc/src/override/malloc.cc"
#include "snmalloc/src/override/new.cc"
```
To enable the `reallocarray` symbol export, this can be added to your cmake command line.
```
-DSNMALLOC_NO_REALLOCARRAY=OFF
```
likewise for `reallocarr`.
```
-DSNMALLOC_NO_REALLOCARR=OFF
```

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");