diff --git a/pw_allocator/BUILD.bazel b/pw_allocator/BUILD.bazel index 6de649822d..12512c3ab2 100644 --- a/pw_allocator/BUILD.bazel +++ b/pw_allocator/BUILD.bazel @@ -119,6 +119,17 @@ pw_cc_library( ], ) +pw_cc_library( + name = "null_allocator", + hdrs = [ + "public/pw_allocator/null_allocator.h", + ], + includes = ["public"], + deps = [ + ":allocator", + ], +) + pw_cc_library( name = "allocator_testing", srcs = [ @@ -196,6 +207,17 @@ pw_cc_test( ], ) +pw_cc_test( + name = "null_allocator_test", + srcs = [ + "null_allocator_test.cc", + ], + deps = [ + ":null_allocator", + "//pw_unit_test", + ], +) + pw_cc_test( name = "split_free_list_allocator_test", srcs = [ diff --git a/pw_allocator/BUILD.gn b/pw_allocator/BUILD.gn index 23106562d8..47f63d7e92 100644 --- a/pw_allocator/BUILD.gn +++ b/pw_allocator/BUILD.gn @@ -103,6 +103,12 @@ pw_source_set("freelist_heap") { sources = [ "freelist_heap.cc" ] } +pw_source_set("null_allocator") { + public_configs = [ ":default_config" ] + public = [ "public/pw_allocator/null_allocator.h" ] + public_deps = [ ":allocator" ] +} + pw_source_set("split_free_list_allocator") { public_configs = [ ":default_config" ] public = [ "public/pw_allocator/split_free_list_allocator.h" ] @@ -124,6 +130,7 @@ pw_test_group("tests") { ":fallback_allocator_test", ":freelist_test", ":freelist_heap_test", + ":null_allocator_test", ":split_free_list_allocator_test", ] } @@ -182,6 +189,11 @@ pw_test("freelist_heap_test") { sources = [ "freelist_heap_test.cc" ] } +pw_test("null_allocator_test") { + deps = [ ":null_allocator" ] + sources = [ "null_allocator_test.cc" ] +} + pw_test("split_free_list_allocator_test") { deps = [ ":split_free_list_allocator", diff --git a/pw_allocator/CMakeLists.txt b/pw_allocator/CMakeLists.txt index 7a45c50309..4462f48e96 100644 --- a/pw_allocator/CMakeLists.txt +++ b/pw_allocator/CMakeLists.txt @@ -97,6 +97,15 @@ pw_add_library(pw_allocator.split_free_list_allocator STATIC pw_bytes ) +pw_add_library(pw_allocator.null_allocator INTERFACE + HEADERS + public/pw_allocator/null_allocator.h + PUBLIC_INCLUDES + public + PUBLIC_DEPS + pw_allocator.allocator +) + pw_add_library(pw_allocator.allocator_testing STATIC HEADERS pw_allocator_private/allocator_testing.h @@ -166,6 +175,17 @@ pw_add_test(pw_allocator.freelist_heap_test pw_allocator ) +pw_add_test(pw_allocator.null_allocator_test + SOURCES + null_allocator_test.cc + PRIVATE_DEPS + pw_allocator.null_allocator + pw_unit_test + GROUPS + modules + pw_allocator +) + pw_add_test(pw_allocator.split_free_list_allocator_test SOURCES split_free_list_allocator_test.cc diff --git a/pw_allocator/docs.rst b/pw_allocator/docs.rst index 27c29383c7..27f2a578de 100644 --- a/pw_allocator/docs.rst +++ b/pw_allocator/docs.rst @@ -35,6 +35,8 @@ Provided implementations of the ``Allocator`` interface include: - ``FallbackAllocator``: Dispatches first to a primary allocator, and, if that fails, to a secondary alloator. +- ``NullAllocator``: Always fails. This may be useful if allocations should be + disallowed under specific circumstances. - ``SplitFreeListAllocator``: Tracks free blocks using a free list, and splits large and small allocations between the front and back, respectively, of its memory region in order to reduce fragmentation. diff --git a/pw_allocator/null_allocator_test.cc b/pw_allocator/null_allocator_test.cc new file mode 100644 index 0000000000..a257e26588 --- /dev/null +++ b/pw_allocator/null_allocator_test.cc @@ -0,0 +1,38 @@ +// Copyright 2023 The Pigweed Authors +// +// 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 "pw_allocator/null_allocator.h" + +#include "gtest/gtest.h" + +namespace pw::allocator { + +TEST(NullAllocatorTest, Allocate) { + NullAllocator allocator; + // Allocate should fail, regardless of size and alignment. + for (size_t size = 1; size < 0x100; size <<= 1) { + for (size_t alignment = 1; alignment < 0x100; alignment <<= 1) { + EXPECT_EQ(allocator.AllocateUnchecked(size, alignment), nullptr); + } + } +} + +TEST(NullAllocatorTest, Resize) { + NullAllocator allocator; + // It is not possible to get a valid pointer from Allocate. + constexpr Layout layout = Layout::Of(); + EXPECT_FALSE(allocator.Resize(this, layout, 1)); +} + +} // namespace pw::allocator diff --git a/pw_allocator/public/pw_allocator/null_allocator.h b/pw_allocator/public/pw_allocator/null_allocator.h new file mode 100644 index 0000000000..55a7f62f43 --- /dev/null +++ b/pw_allocator/public/pw_allocator/null_allocator.h @@ -0,0 +1,41 @@ +// Copyright 2023 The Pigweed Authors +// +// 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. +#pragma once + +#include "pw_allocator/allocator.h" + +namespace pw::allocator { + +/// A memory allocator that always fails to allocate memory. +/// +/// A null allocator may be useful as part of a larger framework if allocation +/// should be disallowed under certain circumstances. For example, a function +/// that returns different allocators based on an input parameter may return a +/// null allocator when given an invalid or unsupported parameter value. +class NullAllocator : public Allocator { + public: + constexpr NullAllocator() = default; + + private: + /// @copydoc Allocator::Allocate + void* DoAllocate(size_t, size_t) override { return nullptr; } + + /// @copydoc Allocator::Deallocate + void DoDeallocate(void*, size_t, size_t) override {} + + /// @copydoc Allocator::Resize + bool DoResize(void*, size_t, size_t, size_t) override { return false; } +}; + +} // namespace pw::allocator