diff --git a/internal/BUILD b/internal/BUILD index bf98a568b..fded67846 100644 --- a/internal/BUILD +++ b/internal/BUILD @@ -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( diff --git a/internal/align.h b/internal/align.h index bdd79fe05..244dcbf44 100644 --- a/internal/align.h +++ b/internal/align.h @@ -27,8 +27,8 @@ namespace cel::internal { template -std::enable_if_t, std::is_unsigned>, - T> +constexpr std::enable_if_t< + std::conjunction_v, std::is_unsigned>, T> AlignmentMask(T alignment) { ABSL_ASSERT(absl::has_single_bit(alignment)); return alignment - T{1}; @@ -73,8 +73,8 @@ std::enable_if_t, T> AlignUp(T x, size_t alignment) { } template -std::enable_if_t, std::is_unsigned>, - bool> +constexpr std::enable_if_t< + std::conjunction_v, std::is_unsigned>, bool> IsAligned(T x, size_t alignment) { ABSL_ASSERT(absl::has_single_bit(alignment)); #if ABSL_HAVE_BUILTIN(__builtin_is_aligned) diff --git a/internal/new.cc b/internal/new.cc new file mode 100644 index 000000000..ce15109e3 --- /dev/null +++ b/internal/new.cc @@ -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 +#include +#include +#include + +#ifdef _MSC_VER +#include +#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(alignment))); +#ifdef CEL_INTERNAL_HAVE_ALIGNED_NEW + return ::operator new(size, alignment); +#else + if (static_cast(alignment) <= kDefaultNewAlignment) { + return New(size); + } +#if defined(_MSC_VER) + void* ptr = _aligned_malloc(size, static_cast(alignment)); + if (ABSL_PREDICT_FALSE(size != 0 && ptr == nullptr)) { + ThrowStdBadAlloc(); + } + return ptr; +#else + void* ptr = std::aligned_alloc(static_cast(alignment), size); + if (ABSL_PREDICT_FALSE(size != 0 && ptr == nullptr)) { + ThrowStdBadAlloc(); + } + return ptr; +#endif +#endif +} + +std::pair SizeReturningNew(size_t size) { + return std::pair{::operator new(size), size}; +} + +std::pair SizeReturningAlignedNew(size_t size, + std::align_val_t alignment) { + ABSL_DCHECK(absl::has_single_bit(static_cast(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(alignment))); +#ifdef CEL_INTERNAL_HAVE_ALIGNED_NEW + ::operator delete(ptr, alignment); +#else + if (static_cast(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(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 diff --git a/internal/new.h b/internal/new.h index 5c7741fe0..3f5dba87a 100644 --- a/internal/new.h +++ b/internal/new.h @@ -21,31 +21,38 @@ namespace cel::internal { -inline std::pair SizeReturningNew(size_t size) { - return std::pair{::operator new(size), size}; -} - -inline std::pair 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 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 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 diff --git a/internal/new_test.cc b/internal/new_test.cc index 16d832e2b..58d885f52 100644 --- a/internal/new_test.cc +++ b/internal/new_test.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include "internal/testing.h" @@ -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(alignof(std::max_align_t) * 2)); + EXPECT_THAT(p, NotNull()); + AlignedDelete(p, + static_cast(alignof(std::max_align_t) * 2)); +} + +TEST(SizeReturningNew, Basic) { void* p; size_t n; std::tie(p, n) = SizeReturningNew(sizeof(uint64_t)); @@ -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(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(alignof(std::max_align_t) * 2)); + SizedAlignedDelete( + p, n, static_cast(alignof(std::max_align_t) * 2)); } } // namespace