Skip to content

Commit

Permalink
pw_allocator: Add LibCAllocator
Browse files Browse the repository at this point in the history
This CL adds LibCAllocator, a memory allocator that uses `malloc` and
`free`.

Change-Id: Idf3f1ce4e4e72ff3c8bff20f3c62ca373f45592e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/172232
Commit-Queue: Aaron Green <[email protected]>
Reviewed-by: Taylor Cramer <[email protected]>
  • Loading branch information
nopsledder authored and CQ Bot Account committed Sep 25, 2023
1 parent 3cfe5d0 commit 2d88135
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 0 deletions.
28 changes: 28 additions & 0 deletions pw_allocator/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,23 @@ pw_cc_library(
],
)

pw_cc_library(
name = "libc_allocator",
srcs = [
"libc_allocator.cc",
],
hdrs = [
"public/pw_allocator/libc_allocator.h",
],
includes = ["public"],
deps = [
":allocator",
"//pw_assert",
"//pw_bytes",
"//pw_status",
],
)

pw_cc_library(
name = "null_allocator",
hdrs = [
Expand Down Expand Up @@ -207,6 +224,17 @@ pw_cc_test(
],
)

pw_cc_test(
name = "libc_allocator_test",
srcs = [
"libc_allocator_test.cc",
],
deps = [
":libc_allocator",
"//pw_unit_test",
],
)

pw_cc_test(
name = "null_allocator_test",
srcs = [
Expand Down
17 changes: 17 additions & 0 deletions pw_allocator/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ pw_source_set("freelist_heap") {
sources = [ "freelist_heap.cc" ]
}

pw_source_set("libc_allocator") {
public_configs = [ ":default_config" ]
public = [ "public/pw_allocator/libc_allocator.h" ]
public_deps = [ ":allocator" ]
deps = [
dir_pw_assert,
dir_pw_bytes,
]
sources = [ "libc_allocator.cc" ]
}

pw_source_set("null_allocator") {
public_configs = [ ":default_config" ]
public = [ "public/pw_allocator/null_allocator.h" ]
Expand Down Expand Up @@ -130,6 +141,7 @@ pw_test_group("tests") {
":fallback_allocator_test",
":freelist_test",
":freelist_heap_test",
":libc_allocator_test",
":null_allocator_test",
":split_free_list_allocator_test",
]
Expand Down Expand Up @@ -189,6 +201,11 @@ pw_test("freelist_heap_test") {
sources = [ "freelist_heap_test.cc" ]
}

pw_test("libc_allocator_test") {
deps = [ ":libc_allocator" ]
sources = [ "libc_allocator_test.cc" ]
}

pw_test("null_allocator_test") {
deps = [ ":null_allocator" ]
sources = [ "null_allocator_test.cc" ]
Expand Down
26 changes: 26 additions & 0 deletions pw_allocator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ pw_add_library(pw_allocator.split_free_list_allocator STATIC
pw_bytes
)

pw_add_library(pw_allocator.libc_allocator STATIC
SOURCES
libc_allocator.cc
HEADERS
public/pw_allocator/libc_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.allocator
pw_status
PRIVATE_DEPS
pw_assert
pw_bytes
)

pw_add_library(pw_allocator.null_allocator INTERFACE
HEADERS
public/pw_allocator/null_allocator.h
Expand Down Expand Up @@ -175,6 +190,17 @@ pw_add_test(pw_allocator.freelist_heap_test
pw_allocator
)

pw_add_test(pw_allocator.libc_allocator_test
SOURCES
libc_allocator_test.cc
PRIVATE_DEPS
pw_allocator.libc_allocator
pw_unit_test
GROUPS
modules
pw_allocator
)

pw_add_test(pw_allocator.null_allocator_test
SOURCES
null_allocator_test.cc
Expand Down
2 changes: 2 additions & 0 deletions pw_allocator/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
- ``LibCAllocator``: Uses ``malloc``, ``realloc``, and ``free``. This should
only be used if the ``libc`` in use provides those functions.
- ``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
Expand Down
52 changes: 52 additions & 0 deletions pw_allocator/libc_allocator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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/libc_allocator.h"

#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <cstring>

#include "pw_assert/check.h"
#include "pw_bytes/alignment.h"

namespace pw::allocator {

void* LibCAllocator::DoAllocate(size_t size, size_t alignment) {
// TODO: b/301930507 - `aligned_alloc` is not portable. Return null for larger
// allocations for now.
return alignment <= alignof(std::max_align_t) ? std::malloc(size) : nullptr;
}

void LibCAllocator::DoDeallocate(void* ptr, size_t, size_t) { std::free(ptr); }

bool LibCAllocator::DoResize(void*, size_t old_size, size_t, size_t new_size) {
// `realloc` may move memory, even when shrinking. Only return true if no
// change is needed.
return old_size == new_size;
}

void* LibCAllocator::DoReallocate(void* ptr,
size_t,
size_t old_alignment,
size_t new_size) {
// TODO: b/301930507 - `aligned_alloc` is not portable. Return null for larger
// allocations for now.
return old_alignment <= alignof(std::max_align_t)
? std::realloc(ptr, new_size)
: nullptr;
}

} // namespace pw::allocator
76 changes: 76 additions & 0 deletions pw_allocator/libc_allocator_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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/libc_allocator.h"

#include <cstring>

#include "gtest/gtest.h"

namespace pw::allocator {

// Test fixtures.

struct LibCAllocatorTest : ::testing::Test {
LibCAllocator allocator;
};

// Unit tests.

TEST_F(LibCAllocatorTest, AllocateDeallocate) {
constexpr Layout layout = Layout::Of<std::byte[64]>();
void* ptr = allocator.Allocate(layout);
ASSERT_NE(ptr, nullptr);
// Check that the pointer can be dereferenced.
memset(ptr, 0xAB, layout.size());
allocator.Deallocate(ptr, layout);
}

TEST_F(LibCAllocatorTest, AllocateLargeAlignment) {
/// TODO: b/301930507 - `aligned_alloc` is not portable. As a result, this
/// allocator has a maximum alignment of `std::align_max_t`.
size_t size = 16;
size_t alignment = alignof(std::max_align_t) * 2;
void* ptr = allocator.AllocateUnchecked(size, alignment);
EXPECT_EQ(ptr, nullptr);
}

TEST_F(LibCAllocatorTest, Resize) {
constexpr Layout old_layout = Layout::Of<uint32_t[4]>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
constexpr Layout new_layout = Layout::Of<uint32_t[3]>();
EXPECT_FALSE(allocator.Resize(ptr, old_layout, new_layout.size()));
allocator.Deallocate(ptr, old_layout);
}

TEST_F(LibCAllocatorTest, ResizeSame) {
constexpr Layout layout = Layout::Of<uint32_t[4]>();
void* ptr = allocator.Allocate(layout);
ASSERT_NE(ptr, nullptr);
EXPECT_TRUE(allocator.Resize(ptr, layout, layout.size()));
allocator.Deallocate(ptr, layout);
}

TEST_F(LibCAllocatorTest, Reallocate) {
constexpr Layout old_layout = Layout::Of<uint32_t[4]>();
void* ptr = allocator.Allocate(old_layout);
ASSERT_NE(ptr, nullptr);
constexpr Layout new_layout = Layout::Of<uint32_t[3]>();
void* new_ptr = allocator.Reallocate(ptr, old_layout, new_layout.size());
ASSERT_NE(new_ptr, nullptr);
allocator.Deallocate(new_ptr, new_layout);
}

} // namespace pw::allocator
48 changes: 48 additions & 0 deletions pw_allocator/public/pw_allocator/libc_allocator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 {

/// Memory allocator that uses `malloc` and `free`.
///
/// TODO: b/301930507 - `aligned_alloc` is not portable. As a result, this
/// allocator has a maximum alignment of `std::align_max_t`.
class LibCAllocator : public Allocator {
public:
constexpr LibCAllocator() = default;

private:
/// @copydoc Allocator::Allocate
void* DoAllocate(size_t size, size_t alignment) override;

/// @copydoc Allocator::Deallocate
void DoDeallocate(void* ptr, size_t size, size_t alignment) override;

/// @copydoc Allocator::Resize
bool DoResize(void* ptr,
size_t old_size,
size_t old_alignment,
size_t new_size) override;

/// @copydoc Allocator::Reallocate
void* DoReallocate(void* ptr,
size_t old_size,
size_t old_alignment,
size_t new_size) override;
};

} // namespace pw::allocator

0 comments on commit 2d88135

Please sign in to comment.