189 lines
5.7 KiB
C++
189 lines
5.7 KiB
C++
// Copyright 2020 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/freelist.h"
|
|
|
|
#include <span>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "pw_status/status.h"
|
|
|
|
using std::byte;
|
|
|
|
namespace pw::allocator {
|
|
|
|
static const size_t SIZE = 8;
|
|
static const std::array<size_t, SIZE> example_sizes = {
|
|
64, 128, 256, 512, 1024, 2048, 4096, 8192};
|
|
|
|
TEST(FreeList, EmptyListHasNoMembers) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
|
|
auto item = list.FindChunk(4);
|
|
EXPECT_EQ(item.size(), static_cast<size_t>(0));
|
|
item = list.FindChunk(128);
|
|
EXPECT_EQ(item.size(), static_cast<size_t>(0));
|
|
}
|
|
|
|
TEST(FreeList, CanRetrieveAddedMember) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN = 512;
|
|
|
|
byte data[kN] = {std::byte(0)};
|
|
|
|
auto status = list.AddChunk(std::span(data, kN));
|
|
EXPECT_EQ(status, OkStatus());
|
|
|
|
auto item = list.FindChunk(kN);
|
|
EXPECT_EQ(item.size(), kN);
|
|
EXPECT_EQ(item.data(), data);
|
|
}
|
|
|
|
TEST(FreeList, CanRetrieveAddedMemberForSmallerSize) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN = 512;
|
|
|
|
byte data[kN] = {std::byte(0)};
|
|
|
|
list.AddChunk(std::span(data, kN))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
auto item = list.FindChunk(kN / 2);
|
|
EXPECT_EQ(item.size(), kN);
|
|
EXPECT_EQ(item.data(), data);
|
|
}
|
|
|
|
TEST(FreeList, CanRemoveItem) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN = 512;
|
|
|
|
byte data[kN] = {std::byte(0)};
|
|
|
|
list.AddChunk(std::span(data, kN))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
auto status = list.RemoveChunk(std::span(data, kN));
|
|
EXPECT_EQ(status, OkStatus());
|
|
|
|
auto item = list.FindChunk(kN);
|
|
EXPECT_EQ(item.size(), static_cast<size_t>(0));
|
|
}
|
|
|
|
TEST(FreeList, FindReturnsSmallestChunk) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN1 = 512;
|
|
constexpr size_t kN2 = 1024;
|
|
|
|
byte data1[kN1] = {std::byte(0)};
|
|
byte data2[kN2] = {std::byte(0)};
|
|
|
|
list.AddChunk(std::span(data1, kN1))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
list.AddChunk(std::span(data2, kN2))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
|
|
auto chunk = list.FindChunk(kN1 / 2);
|
|
EXPECT_EQ(chunk.size(), kN1);
|
|
EXPECT_EQ(chunk.data(), data1);
|
|
|
|
chunk = list.FindChunk(kN1);
|
|
EXPECT_EQ(chunk.size(), kN1);
|
|
EXPECT_EQ(chunk.data(), data1);
|
|
|
|
chunk = list.FindChunk(kN1 + 1);
|
|
EXPECT_EQ(chunk.size(), kN2);
|
|
EXPECT_EQ(chunk.data(), data2);
|
|
}
|
|
|
|
TEST(FreeList, FindReturnsCorrectChunkInSameBucket) {
|
|
// If we have two values in the same bucket, ensure that the allocation will
|
|
// pick an appropriately sized one.
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN1 = 512;
|
|
constexpr size_t kN2 = 257;
|
|
|
|
byte data1[kN1] = {std::byte(0)};
|
|
byte data2[kN2] = {std::byte(0)};
|
|
|
|
// List should now be 257 -> 512 -> NULL
|
|
list.AddChunk(std::span(data1, kN1))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
list.AddChunk(std::span(data2, kN2))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
|
|
auto chunk = list.FindChunk(kN2 + 1);
|
|
EXPECT_EQ(chunk.size(), kN1);
|
|
}
|
|
|
|
TEST(FreeList, FindCanMoveUpThroughBuckets) {
|
|
// Ensure that finding a chunk will move up through buckets if no appropriate
|
|
// chunks were found in a given bucket
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN1 = 257;
|
|
constexpr size_t kN2 = 513;
|
|
|
|
byte data1[kN1] = {std::byte(0)};
|
|
byte data2[kN2] = {std::byte(0)};
|
|
|
|
// List should now be:
|
|
// bkt[3] (257 bytes up to 512 bytes) -> 257 -> NULL
|
|
// bkt[4] (513 bytes up to 1024 bytes) -> 513 -> NULL
|
|
list.AddChunk(std::span(data1, kN1))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
list.AddChunk(std::span(data2, kN2))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
|
|
// Request a 300 byte chunk. This should return the 513 byte one
|
|
auto chunk = list.FindChunk(kN1 + 1);
|
|
EXPECT_EQ(chunk.size(), kN2);
|
|
}
|
|
|
|
TEST(FreeList, RemoveUnknownChunkReturnsNotFound) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN = 512;
|
|
|
|
byte data[kN] = {std::byte(0)};
|
|
byte data2[kN] = {std::byte(0)};
|
|
|
|
list.AddChunk(std::span(data, kN))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
auto status = list.RemoveChunk(std::span(data2, kN));
|
|
EXPECT_EQ(status, Status::NotFound());
|
|
}
|
|
|
|
TEST(FreeList, CanStoreMultipleChunksPerBucket) {
|
|
FreeListBuffer<SIZE> list(example_sizes);
|
|
constexpr size_t kN = 512;
|
|
|
|
byte data1[kN] = {std::byte(0)};
|
|
byte data2[kN] = {std::byte(0)};
|
|
|
|
list.AddChunk(std::span(data1, kN))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
list.AddChunk(std::span(data2, kN))
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
|
|
auto chunk1 = list.FindChunk(kN);
|
|
list.RemoveChunk(chunk1)
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
auto chunk2 = list.FindChunk(kN);
|
|
list.RemoveChunk(chunk2)
|
|
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
|
|
|
|
// Ordering of the chunks doesn't matter
|
|
EXPECT_TRUE(chunk1.data() != chunk2.data());
|
|
EXPECT_TRUE(chunk1.data() == data1 || chunk1.data() == data2);
|
|
EXPECT_TRUE(chunk2.data() == data1 || chunk2.data() == data2);
|
|
}
|
|
|
|
} // namespace pw::allocator
|