Skip to content

Commit

Permalink
Factor out libc code into a header. (#624)
Browse files Browse the repository at this point in the history
* Factor out libc code into a header.

This pulls the main definitions of the various libc malloc functions
into a header for easier use and inclusion in other projects.

* Clang-tidy fixes.

* Clang-tidy fixes really.

* More code quality changes

* Minor fix

* Clangformat
  • Loading branch information
mjp41 authored Aug 9, 2023
1 parent 9d44660 commit 6cbc50f
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 171 deletions.
4 changes: 3 additions & 1 deletion src/snmalloc/ds_core/redblacktree.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ namespace snmalloc

template<typename Rep>
concept RBRep = //
RBRepTypes<Rep> && RBRepMethods<Rep> &&
RBRepTypes<Rep> //
&& RBRepMethods<Rep> //
&&
ConceptSame<decltype(Rep::null), std::add_const_t<typename Rep::Contents>>;
#endif

Expand Down
9 changes: 8 additions & 1 deletion src/snmalloc/mem/localalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ namespace snmalloc
}

return check_init([&](CoreAlloc* core_alloc) {
if (size > bits::one_at_bit(bits::BITS - 1))
{
// Cannot allocate something that is more that half the size of the
// address space
errno = ENOMEM;
return capptr::Alloc<void>{nullptr};
}
// Grab slab of correct size
// Set remote as large allocator remote.
auto [chunk, meta] = Config::Backend::alloc_chunk(
Expand Down Expand Up @@ -712,7 +719,7 @@ namespace snmalloc
auto pm_size = sizeclass_full_to_size(pm_sc);
snmalloc_check_client(
mitigations(sanity_checks),
sc == pm_sc,
(sc == pm_sc) || (p == nullptr),
"Dealloc rounded size mismatch: {} != {}",
rsize,
pm_size);
Expand Down
6 changes: 6 additions & 0 deletions src/snmalloc/mem/sizeclasstable.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ namespace snmalloc
{
if (size > sizeclass_to_size(NUM_SMALL_SIZECLASSES - 1))
{
if (size > bits::one_at_bit(bits::BITS - 1))
{
// This size is too large, no rounding should occur as will result in a
// failed allocation later.
return size;
}
return bits::next_pow2(size);
}
// If realloc(ptr, 0) returns nullptr, some consumers treat this as a
Expand Down
177 changes: 177 additions & 0 deletions src/snmalloc/override/libc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#pragma once

#include "../global/global.h"

#include <errno.h>
#include <string.h>

namespace snmalloc::libc
{
SNMALLOC_SLOW_PATH inline void* set_error(int err = ENOMEM)
{
errno = err;
return nullptr;
}

SNMALLOC_SLOW_PATH inline int set_error_and_return(int err = ENOMEM)
{
errno = err;
return err;
}

inline void* __malloc_end_pointer(void* ptr)
{
return ThreadAlloc::get().external_pointer<OnePastEnd>(ptr);
}

SNMALLOC_FAST_PATH_INLINE void* malloc(size_t size)
{
return ThreadAlloc::get().alloc(size);
}

SNMALLOC_FAST_PATH_INLINE void free(void* ptr)
{
ThreadAlloc::get().dealloc(ptr);
}

SNMALLOC_FAST_PATH_INLINE void free_sized(void* ptr, size_t size)
{
ThreadAlloc::get().dealloc(ptr, size);
}

SNMALLOC_FAST_PATH_INLINE void* calloc(size_t nmemb, size_t size)
{
bool overflow = false;
size_t sz = bits::umul(size, nmemb, overflow);
if (SNMALLOC_UNLIKELY(overflow))
{
return set_error();
}
return ThreadAlloc::get().alloc<ZeroMem::YesZero>(sz);
}

SNMALLOC_FAST_PATH_INLINE void* realloc(void* ptr, size_t size)
{
auto& a = ThreadAlloc::get();
size_t sz = a.alloc_size(ptr);
// Keep the current allocation if the given size is in the same sizeclass.
if (sz == round_size(size))
{
#ifdef SNMALLOC_PASS_THROUGH
// snmallocs alignment guarantees can be broken by realloc in pass-through
// this is not exercised, by existing clients, but is tested.
if (pointer_align_up(ptr, natural_alignment(size)) == ptr)
return ptr;
#else
return ptr;
#endif
}

void* p = a.alloc(size);
if (SNMALLOC_LIKELY(p != nullptr))
{
sz = bits::min(size, sz);
// Guard memcpy as GCC is assuming not nullptr for ptr after the memcpy
// otherwise.
if (SNMALLOC_UNLIKELY(sz != 0))
::memcpy(p, ptr, sz);
a.dealloc(ptr);
}
else if (SNMALLOC_LIKELY(size == 0))
{
a.dealloc(ptr);
}
else
{
return set_error();
}
return p;
}

inline size_t malloc_usable_size(const void* ptr)
{
return ThreadAlloc::get().alloc_size(ptr);
}

inline void* reallocarray(void* ptr, size_t nmemb, size_t size)
{
bool overflow = false;
size_t sz = bits::umul(size, nmemb, overflow);
if (SNMALLOC_UNLIKELY(overflow))
{
return set_error();
}
return realloc(ptr, sz);
}

inline int 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 (SNMALLOC_UNLIKELY(sz == 0))
{
errno = err;
return 0;
}
if (SNMALLOC_UNLIKELY(overflow))
{
return set_error_and_return(EOVERFLOW);
}

void** ptr = reinterpret_cast<void**>(ptr_);
void* p = a.alloc(sz);
if (SNMALLOC_UNLIKELY(p == nullptr))
{
return set_error_and_return(ENOMEM);
}

sz = bits::min(sz, a.alloc_size(*ptr));

SNMALLOC_ASSUME(*ptr != nullptr || sz == 0);
// Guard memcpy as GCC is assuming not nullptr for ptr after the memcpy
// otherwise.
if (SNMALLOC_UNLIKELY(sz != 0))
::memcpy(p, *ptr, sz);
errno = err;
a.dealloc(*ptr);
*ptr = p;
return 0;
}

inline void* memalign(size_t alignment, size_t size)
{
if (SNMALLOC_UNLIKELY(
alignment < sizeof(uintptr_t) || !bits::is_pow2(alignment)))
{
return set_error(EINVAL);
}

return malloc(aligned_size(alignment, size));
}

inline void* aligned_alloc(size_t alignment, size_t size)
{
SNMALLOC_ASSERT((size % alignment) == 0);
return memalign(alignment, size);
}

inline int posix_memalign(void** memptr, size_t alignment, size_t size)
{
if (SNMALLOC_UNLIKELY(
(alignment < sizeof(uintptr_t) || !bits::is_pow2(alignment))))
{
return EINVAL;
}

void* p = memalign(alignment, size);
if (SNMALLOC_UNLIKELY(p == nullptr))
{
if (size != 0)
return ENOMEM;
}
*memptr = p;
return 0;
}
} // namespace snmalloc::libc
Loading

0 comments on commit 6cbc50f

Please sign in to comment.