| // 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 |