blob: 675e34c273bbfd52d691e4aebff6afd50544a43a [file] [log] [blame]
// 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/multiplex_allocator.h"
#include "pw_allocator/allocator_testing.h"
#include "pw_tokenizer/tokenize.h"
#include "pw_unit_test/framework.h"
namespace pw::allocator {
namespace {
// Test fixtures.
constexpr size_t kHeapSize = 256;
// Token and layout as might be defined by an application.
constexpr tokenizer::Token kFooToken = PW_TOKENIZE_STRING("foo");
constexpr Layout kFooLayout(32, 16);
// Token and layout distinct from those above.
constexpr tokenizer::Token kBarToken = PW_TOKENIZE_STRING("bar");
constexpr Layout kBarLayout(128, 4);
// Token that will be mapped to the same allocator as `kBarToken`.
constexpr tokenizer::Token kBazToken = PW_TOKENIZE_STRING("baz");
// Token that will be explicitly mapped to nothing.
constexpr tokenizer::Token kQuxToken = PW_TOKENIZE_STRING("qux");
// Token that isn't recognized by the multiplex allocator.
constexpr tokenizer::Token kInvalidToken = PW_TOKENIZE_STRING("invalid");
/// Types to use for suballocators.
using Suballocator = test::AllocatorForTest<kHeapSize>;
/// Helper method to restore suballocator type, in order to make test methods
/// easier to access.
template <typename MultiplexAllocatorType>
Suballocator* GetSuballocator(MultiplexAllocatorType& allocator,
tokenizer::Token token) {
return static_cast<Suballocator*>(allocator.GetAllocator(token));
}
/// Represents a MultiplexAllocator with custom logic.
class CustomMultiplexAllocator : public MultiplexAllocator {
public:
CustomMultiplexAllocator(Allocator* foo, Allocator* bar)
: MultiplexAllocator(), foo_(foo), bar_(bar) {}
private:
Allocator* DoGetAllocator(Token token) const override {
switch (token) {
case kFooToken:
return foo_;
case kBarToken:
case kBazToken:
return bar_;
case kQuxToken:
default:
return nullptr;
}
}
Allocator* foo_;
Allocator* bar_;
};
// Unit tests
template <typename MultiplexAllocatorType>
void GetAllocator(MultiplexAllocatorType& allocator,
Allocator* foo,
Allocator* bar) {
EXPECT_EQ(allocator.GetAllocator(kFooToken), foo);
EXPECT_EQ(allocator.GetAllocator(kBarToken), bar);
EXPECT_EQ(allocator.GetAllocator(kBazToken), bar);
EXPECT_EQ(allocator.GetAllocator(kQuxToken), nullptr);
EXPECT_EQ(allocator.GetAllocator(kInvalidToken), nullptr);
}
TEST(MultiplexAllocatorTest, GetAllocator) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
GetAllocator(allocator, &foo, &bar);
}
TEST(MultiplexAllocatorTest, GetAllocatorCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
GetAllocator(allocator, &foo, &bar);
}
template <typename MultiplexAllocatorType>
void AllocateValidToken(MultiplexAllocatorType& allocator) {
EXPECT_NE(allocator.Allocate(kFooToken, kFooLayout), nullptr);
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->allocate_size(), kFooLayout.size());
EXPECT_NE(allocator.Allocate(kBarToken, kBarLayout), nullptr);
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->allocate_size(), kBarLayout.size());
}
TEST(MultiplexAllocatorTest, AllocateValidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
AllocateValidToken(allocator);
}
TEST(MultiplexAllocatorTest, AllocateValidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
AllocateValidToken(allocator);
}
template <typename MultiplexAllocatorType>
void AllocateInvalidToken(MultiplexAllocatorType& allocator) {
EXPECT_EQ(allocator.Allocate(kQuxToken, kFooLayout), nullptr);
EXPECT_EQ(allocator.Allocate(kInvalidToken, kBarLayout), nullptr);
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->allocate_size(), 0U);
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->allocate_size(), 0U);
}
TEST(MultiplexAllocatorTest, AllocateInvalidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
AllocateInvalidToken(allocator);
}
TEST(MultiplexAllocatorTest, AllocateInvalidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
AllocateInvalidToken(allocator);
}
template <typename MultiplexAllocatorType>
void DeallocateValidToken(MultiplexAllocatorType& allocator) {
void* foo_ptr = allocator.Allocate(kFooToken, kFooLayout);
void* bar_ptr = allocator.Allocate(kBarToken, kBarLayout);
allocator.Deallocate(kFooToken, foo_ptr, kFooLayout);
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->deallocate_ptr(), foo_ptr);
EXPECT_EQ(foo->deallocate_size(), kFooLayout.size());
allocator.Deallocate(kBarToken, bar_ptr, kBarLayout);
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->deallocate_ptr(), bar_ptr);
EXPECT_EQ(bar->deallocate_size(), kBarLayout.size());
}
TEST(MultiplexAllocatorTest, DeallocateValidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
DeallocateValidToken(allocator);
}
TEST(MultiplexAllocatorTest, DeallocateValidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
DeallocateValidToken(allocator);
}
template <typename MultiplexAllocatorType>
void DeallocateInvalidToken(MultiplexAllocatorType& allocator) {
void* foo_ptr = allocator.Allocate(kFooToken, kFooLayout);
void* bar_ptr = allocator.Allocate(kBarToken, kBarLayout);
allocator.Deallocate(kQuxToken, foo_ptr, kFooLayout);
allocator.Deallocate(kInvalidToken, bar_ptr, kBarLayout);
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->deallocate_ptr(), nullptr);
EXPECT_EQ(foo->deallocate_size(), 0U);
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->deallocate_ptr(), nullptr);
EXPECT_EQ(bar->deallocate_size(), 0U);
}
TEST(MultiplexAllocatorTest, DeallocateInvalidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
DeallocateInvalidToken(allocator);
}
TEST(MultiplexAllocatorTest, DeallocateInvalidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
DeallocateInvalidToken(allocator);
}
template <typename MultiplexAllocatorType>
void ResizeValidToken(MultiplexAllocatorType& allocator) {
void* foo_ptr = allocator.Allocate(kFooToken, kFooLayout);
void* bar_ptr = allocator.Allocate(kBarToken, kBarLayout);
EXPECT_TRUE(
allocator.Resize(kFooToken, foo_ptr, kFooLayout, kFooLayout.size() * 2));
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->resize_ptr(), foo_ptr);
EXPECT_EQ(foo->resize_old_size(), kFooLayout.size());
EXPECT_EQ(foo->resize_new_size(), kFooLayout.size() * 2);
EXPECT_TRUE(
allocator.Resize(kBarToken, bar_ptr, kBarLayout, kBarLayout.size() / 2));
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->resize_ptr(), bar_ptr);
EXPECT_EQ(bar->resize_old_size(), kBarLayout.size());
EXPECT_EQ(bar->resize_new_size(), kBarLayout.size() / 2);
}
TEST(MultiplexAllocatorTest, ResizeValidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
ResizeValidToken(allocator);
}
TEST(MultiplexAllocatorTest, ResizeValidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
ResizeValidToken(allocator);
}
template <typename MultiplexAllocatorType>
void ResizeInvalidToken(MultiplexAllocatorType& allocator) {
void* foo_ptr = allocator.Allocate(kFooToken, kFooLayout);
void* bar_ptr = allocator.Allocate(kBarToken, kBarLayout);
EXPECT_FALSE(
allocator.Resize(kQuxToken, foo_ptr, kFooLayout, kFooLayout.size() / 2));
EXPECT_FALSE(allocator.Resize(
kInvalidToken, bar_ptr, kBarLayout, kBarLayout.size() / 2));
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->resize_ptr(), nullptr);
EXPECT_EQ(foo->resize_old_size(), 0U);
EXPECT_EQ(foo->resize_new_size(), 0U);
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->resize_ptr(), nullptr);
EXPECT_EQ(bar->resize_old_size(), 0U);
EXPECT_EQ(bar->resize_new_size(), 0U);
}
TEST(MultiplexAllocatorTest, ResizeInvalidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
ResizeInvalidToken(allocator);
}
TEST(MultiplexAllocatorTest, ResizeInvalidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
ResizeInvalidToken(allocator);
}
template <typename MultiplexAllocatorType>
void ReallocateValidToken(MultiplexAllocatorType& allocator) {
void* foo_ptr = allocator.Allocate(kFooToken, kFooLayout);
void* bar_ptr = allocator.Allocate(kBarToken, kBarLayout);
EXPECT_NE(allocator.Reallocate(
kFooToken, foo_ptr, kFooLayout, kFooLayout.size() * 2),
nullptr);
auto* foo = GetSuballocator(allocator, kFooToken);
EXPECT_EQ(foo->resize_ptr(), foo_ptr);
EXPECT_EQ(foo->resize_old_size(), kFooLayout.size());
EXPECT_EQ(foo->resize_new_size(), kFooLayout.size() * 2);
EXPECT_NE(allocator.Reallocate(
kBarToken, bar_ptr, kBarLayout, kBarLayout.size() / 2),
nullptr);
auto* bar = GetSuballocator(allocator, kBarToken);
EXPECT_EQ(bar->resize_ptr(), bar_ptr);
EXPECT_EQ(bar->resize_old_size(), kBarLayout.size());
EXPECT_EQ(bar->resize_new_size(), kBarLayout.size() / 2);
}
TEST(MultiplexAllocatorTest, ReallocateValidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
ReallocateValidToken(allocator);
}
TEST(MultiplexAllocatorTest, ReallocateValidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
ReallocateValidToken(allocator);
}
template <typename MultiplexAllocatorType>
void ReallocateInvalidToken(MultiplexAllocatorType& allocator) {
void* foo_ptr = allocator.Allocate(kFooToken, kFooLayout);
auto* foo = GetSuballocator(allocator, kFooToken);
foo->ResetParameters();
void* bar_ptr = allocator.Allocate(kBarToken, kBarLayout);
auto* bar = GetSuballocator(allocator, kBarToken);
bar->ResetParameters();
EXPECT_EQ(allocator.Reallocate(
kQuxToken, foo_ptr, kFooLayout, kFooLayout.size() * 2),
nullptr);
EXPECT_EQ(allocator.Reallocate(
kInvalidToken, bar_ptr, kBarLayout, kBarLayout.size() / 2),
nullptr);
EXPECT_EQ(foo->allocate_size(), 0U);
EXPECT_EQ(foo->resize_ptr(), nullptr);
EXPECT_EQ(foo->resize_old_size(), 0U);
EXPECT_EQ(foo->resize_new_size(), 0U);
EXPECT_EQ(bar->allocate_size(), 0U);
EXPECT_EQ(bar->resize_ptr(), nullptr);
EXPECT_EQ(bar->resize_old_size(), 0U);
EXPECT_EQ(bar->resize_new_size(), 0U);
}
TEST(MultiplexAllocatorTest, ReallocateInvalidToken) {
Suballocator foo, bar;
FlatMapMultiplexAllocator<4> allocator({{{kFooToken, &foo},
{kBarToken, &bar},
{kBazToken, &bar},
{kQuxToken, nullptr}}});
ReallocateInvalidToken(allocator);
}
TEST(MultiplexAllocatorTest, ReallocateInvalidTokenCustom) {
Suballocator foo, bar;
CustomMultiplexAllocator allocator(&foo, &bar);
ReallocateInvalidToken(allocator);
}
} // namespace
} // namespace pw::allocator