1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 18 #include "pw_allocator/allocator.h" 19 #include "pw_allocator/block.h" 20 #include "pw_allocator/buffer.h" 21 #include "pw_allocator/first_fit_block_allocator.h" 22 #include "pw_allocator/metrics.h" 23 #include "pw_allocator/tracking_allocator.h" 24 #include "pw_bytes/span.h" 25 #include "pw_result/result.h" 26 #include "pw_status/status.h" 27 #include "pw_tokenizer/tokenize.h" 28 #include "pw_unit_test/framework.h" 29 30 namespace pw::allocator { 31 namespace test { 32 33 // A token that can be used in tests. 34 constexpr pw::tokenizer::Token kToken = PW_TOKENIZE_STRING("test"); 35 36 /// An `AllocatorForTest` that is automatically initialized on construction. 37 template <size_t kBufferSize, typename MetricsType = internal::AllMetrics> 38 class AllocatorForTest : public Allocator { 39 public: 40 using AllocatorType = FirstFitBlockAllocator<uint32_t>; 41 using BlockType = AllocatorType::BlockType; 42 AllocatorForTest()43 AllocatorForTest() 44 : Allocator(AllocatorType::kCapabilities), tracker_(kToken, *allocator_) { 45 ResetParameters(); 46 allocator_->Init(allocator_.as_bytes()); 47 } 48 ~AllocatorForTest()49 ~AllocatorForTest() override { 50 for (auto* block : allocator_->blocks()) { 51 BlockType::Free(block); 52 } 53 allocator_->Reset(); 54 } 55 blocks()56 AllocatorType::Range blocks() const { return allocator_->blocks(); } blocks()57 AllocatorType::Range blocks() { return allocator_->blocks(); } 58 metric_group()59 const metric::Group& metric_group() const { return tracker_.metric_group(); } metric_group()60 metric::Group& metric_group() { return tracker_.metric_group(); } 61 metrics()62 const MetricsType& metrics() const { return tracker_.metrics(); } 63 allocate_size()64 size_t allocate_size() const { return allocate_size_; } deallocate_ptr()65 void* deallocate_ptr() const { return deallocate_ptr_; } deallocate_size()66 size_t deallocate_size() const { return deallocate_size_; } resize_ptr()67 void* resize_ptr() const { return resize_ptr_; } resize_old_size()68 size_t resize_old_size() const { return resize_old_size_; } resize_new_size()69 size_t resize_new_size() const { return resize_new_size_; } 70 71 /// Resets the recorded parameters to an initial state. ResetParameters()72 void ResetParameters() { 73 allocate_size_ = 0; 74 deallocate_ptr_ = nullptr; 75 deallocate_size_ = 0; 76 resize_ptr_ = nullptr; 77 resize_old_size_ = 0; 78 resize_new_size_ = 0; 79 } 80 81 /// Allocates all the memory from this object. Exhaust()82 void Exhaust() { 83 for (auto* block : allocator_->blocks()) { 84 block->MarkUsed(); 85 } 86 } 87 88 private: 89 /// @copydoc Allocator::Allocate DoAllocate(Layout layout)90 void* DoAllocate(Layout layout) override { 91 allocate_size_ = layout.size(); 92 return tracker_.Allocate(layout); 93 } 94 95 /// @copydoc Allocator::Deallocate DoDeallocate(void * ptr)96 void DoDeallocate(void* ptr) override { 97 Result<Layout> requested = GetRequestedLayout(tracker_, ptr); 98 deallocate_ptr_ = ptr; 99 deallocate_size_ = requested.ok() ? requested->size() : 0; 100 tracker_.Deallocate(ptr); 101 } 102 103 /// @copydoc Allocator::Deallocate DoDeallocate(void * ptr,Layout)104 void DoDeallocate(void* ptr, Layout) override { DoDeallocate(ptr); } 105 106 /// @copydoc Allocator::Resize DoResize(void * ptr,size_t new_size)107 bool DoResize(void* ptr, size_t new_size) override { 108 Result<Layout> requested = GetRequestedLayout(tracker_, ptr); 109 resize_ptr_ = ptr; 110 resize_old_size_ = requested.ok() ? requested->size() : 0; 111 resize_new_size_ = new_size; 112 return tracker_.Resize(ptr, new_size); 113 } 114 115 /// @copydoc Deallocator::GetInfo DoGetInfo(InfoType info_type,const void * ptr)116 Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) const override { 117 return GetInfo(tracker_, info_type, ptr); 118 } 119 120 WithBuffer<AllocatorType, kBufferSize> allocator_; 121 TrackingAllocator<MetricsType> tracker_; 122 size_t allocate_size_; 123 void* deallocate_ptr_; 124 size_t deallocate_size_; 125 void* resize_ptr_; 126 size_t resize_old_size_; 127 size_t resize_new_size_; 128 }; 129 130 } // namespace test 131 } // namespace pw::allocator 132