Skip to content

Commit

Permalink
More comprehensive wrappers for operator new and operator delete
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 639829496
  • Loading branch information
jcking authored and copybara-github committed Jun 3, 2024
1 parent b10fc7f commit abbbf51
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 31 deletions.
8 changes: 8 additions & 0 deletions internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,15 @@ cc_test(

cc_library(
name = "new",
srcs = ["new.cc"],
hdrs = ["new.h"],
deps = [
":align",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/numeric:bits",
],
)

cc_test(
Expand Down
8 changes: 4 additions & 4 deletions internal/align.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
namespace cel::internal {

template <typename T>
std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_unsigned<T>>,
T>
constexpr std::enable_if_t<
std::conjunction_v<std::is_integral<T>, std::is_unsigned<T>>, T>
AlignmentMask(T alignment) {
ABSL_ASSERT(absl::has_single_bit(alignment));
return alignment - T{1};
Expand Down Expand Up @@ -73,8 +73,8 @@ std::enable_if_t<std::is_pointer_v<T>, T> AlignUp(T x, size_t alignment) {
}

template <typename T>
std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_unsigned<T>>,
bool>
constexpr std::enable_if_t<
std::conjunction_v<std::is_integral<T>, std::is_unsigned<T>>, bool>
IsAligned(T x, size_t alignment) {
ABSL_ASSERT(absl::has_single_bit(alignment));
#if ABSL_HAVE_BUILTIN(__builtin_is_aligned)
Expand Down
140 changes: 140 additions & 0 deletions internal/new.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "internal/new.h"

#include <cstddef>
#include <cstdlib>
#include <new>
#include <utility>

#ifdef _MSC_VER
#include <malloc.h>
#endif

#include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/log/absl_check.h"
#include "absl/numeric/bits.h"
#include "internal/align.h"

#if defined(__cpp_aligned_new) && __cpp_aligned_new >= 201606L
#define CEL_INTERNAL_HAVE_ALIGNED_NEW 1
#endif

#if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
#define CEL_INTERNAL_HAVE_SIZED_DELETE 1
#endif

namespace cel::internal {

namespace {

[[noreturn]] void ThrowStdBadAlloc() {
#ifdef ABSL_HAVE_EXCEPTIONS
throw std::bad_alloc();
#else
std::abort();
#endif
}

} // namespace

#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
static_assert(IsAligned(alignof(std::max_align_t),
__STDCPP_DEFAULT_NEW_ALIGNMENT__));
#endif

void* New(size_t size) { return ::operator new(size); }

void* AlignedNew(size_t size, std::align_val_t alignment) {
ABSL_DCHECK(absl::has_single_bit(static_cast<size_t>(alignment)));
#ifdef CEL_INTERNAL_HAVE_ALIGNED_NEW
return ::operator new(size, alignment);
#else
if (static_cast<size_t>(alignment) <= kDefaultNewAlignment) {
return New(size);
}
#if defined(_MSC_VER)
void* ptr = _aligned_malloc(size, static_cast<size_t>(alignment));
if (ABSL_PREDICT_FALSE(size != 0 && ptr == nullptr)) {
ThrowStdBadAlloc();
}
return ptr;
#else
void* ptr = std::aligned_alloc(static_cast<size_t>(alignment), size);
if (ABSL_PREDICT_FALSE(size != 0 && ptr == nullptr)) {
ThrowStdBadAlloc();
}
return ptr;
#endif
#endif
}

std::pair<void*, size_t> SizeReturningNew(size_t size) {
return std::pair{::operator new(size), size};
}

std::pair<void*, size_t> SizeReturningAlignedNew(size_t size,
std::align_val_t alignment) {
ABSL_DCHECK(absl::has_single_bit(static_cast<size_t>(alignment)));
#ifdef CEL_INTERNAL_HAVE_ALIGNED_NEW
return std::pair{::operator new(size, alignment), size};
#else
return std::pair{AlignedNew(size, alignment), size};
#endif
}

void Delete(void* ptr) noexcept { ::operator delete(ptr); }

void SizedDelete(void* ptr, size_t size) noexcept {
#ifdef CEL_INTERNAL_HAVE_SIZED_DELETE
::operator delete(ptr, size);
#else
::operator delete(ptr);
#endif
}

void AlignedDelete(void* ptr, std::align_val_t alignment) noexcept {
ABSL_DCHECK(absl::has_single_bit(static_cast<size_t>(alignment)));
#ifdef CEL_INTERNAL_HAVE_ALIGNED_NEW
::operator delete(ptr, alignment);
#else
if (static_cast<size_t>(alignment) <= kDefaultNewAlignment) {
Delete(ptr, size);
} else {
#if defined(_MSC_VER)
_aligned_free(ptr);
#else
std::free(ptr);
#endif
}
#endif
}

void SizedAlignedDelete(void* ptr, size_t size,
std::align_val_t alignment) noexcept {
ABSL_DCHECK(absl::has_single_bit(static_cast<size_t>(alignment)));
#ifdef CEL_INTERNAL_HAVE_ALIGNED_NEW
#ifdef CEL_INTERNAL_HAVE_SIZED_DELETE
::operator delete(ptr, size, alignment);
#else
::operator delete(ptr, alignment);
#endif
#else
AlignedDelete(ptr, alignment);
#endif
}

} // namespace cel::internal
51 changes: 29 additions & 22 deletions internal/new.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,38 @@

namespace cel::internal {

inline std::pair<void*, size_t> SizeReturningNew(size_t size) {
return std::pair{::operator new(size), size};
}

inline std::pair<void*, size_t> SizeReturningNew(size_t size,
std::align_val_t alignment) {
return std::pair{::operator new(size, alignment), size};
}

inline void SizedDelete(void* ptr, size_t size) noexcept {
#if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
::operator delete(ptr, size);
inline constexpr size_t kDefaultNewAlignment =
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
__STDCPP_DEFAULT_NEW_ALIGNMENT__
#else
::operator delete(ptr);
alignof(std::max_align_t)
#endif
}
; // NOLINT(whitespace/semicolon)

inline void SizedDelete(void* ptr, size_t size,
std::align_val_t alignment) noexcept {
#if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
::operator delete(ptr, size, alignment);
#else
::operator delete(ptr, alignment);
#endif
}
void* New(size_t size);

// Allocates memory which has a size of at least `size` and a minimum alignment
// of `alignment`. To deallocate, the caller must use `AlignedDelete` or
// `SizedAlignedDelete`.
void* AlignedNew(size_t size, std::align_val_t alignment);

std::pair<void*, size_t> SizeReturningNew(size_t size);

// Allocates memory which has a size of at least `size` and a minimum alignment
// of `alignment`, returns a pointer to the allocated memory and the actual
// usable allocation size. To deallocate, the caller must use `AlignedDelete` or
// `SizedAlignedDelete`.
std::pair<void*, size_t> SizeReturningAlignedNew(size_t size,
std::align_val_t alignment);

void Delete(void* ptr) noexcept;

void SizedDelete(void* ptr, size_t size) noexcept;

void AlignedDelete(void* ptr, std::align_val_t alignment) noexcept;

void SizedAlignedDelete(void* ptr, size_t size,
std::align_val_t alignment) noexcept;

} // namespace cel::internal

Expand Down
26 changes: 21 additions & 5 deletions internal/new_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <cstddef>
#include <cstdint>
#include <new>
#include <tuple>

#include "internal/testing.h"
Expand All @@ -26,7 +27,22 @@ namespace {
using testing::Ge;
using testing::NotNull;

TEST(Sized, Normal) {
TEST(New, Basic) {
void* p = New(sizeof(uint64_t));
EXPECT_THAT(p, NotNull());
Delete(p);
}

TEST(AlignedNew, Basic) {
void* p =
AlignedNew(alignof(std::max_align_t) * 2,
static_cast<std::align_val_t>(alignof(std::max_align_t) * 2));
EXPECT_THAT(p, NotNull());
AlignedDelete(p,
static_cast<std::align_val_t>(alignof(std::max_align_t) * 2));
}

TEST(SizeReturningNew, Basic) {
void* p;
size_t n;
std::tie(p, n) = SizeReturningNew(sizeof(uint64_t));
Expand All @@ -35,16 +51,16 @@ TEST(Sized, Normal) {
SizedDelete(p, n);
}

TEST(Sized, Overaligned) {
TEST(SizeReturningAlignedNew, Basic) {
void* p;
size_t n;
std::tie(p, n) = SizeReturningNew(
std::tie(p, n) = SizeReturningAlignedNew(
alignof(std::max_align_t) * 2,
static_cast<std::align_val_t>(alignof(std::max_align_t) * 2));
EXPECT_THAT(p, NotNull());
EXPECT_THAT(n, Ge(alignof(std::max_align_t) * 2));
SizedDelete(p, n,
static_cast<std::align_val_t>(alignof(std::max_align_t) * 2));
SizedAlignedDelete(
p, n, static_cast<std::align_val_t>(alignof(std::max_align_t) * 2));
}

} // namespace
Expand Down

0 comments on commit abbbf51

Please sign in to comment.