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:
@@ -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>)
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user