• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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