1 // Copyright 2024 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
15 #include "pw_allocator/chunk_pool.h"
16
17 #include "pw_allocator/buffer.h"
18 #include "pw_assert/check.h"
19 #include "pw_bytes/alignment.h"
20
21 namespace pw::allocator {
22
EnsurePointerLayout(const Layout & layout)23 static Layout EnsurePointerLayout(const Layout& layout) {
24 return Layout(std::max(layout.size(), sizeof(void*)),
25 std::max(layout.alignment(), alignof(void*)));
26 }
27
ChunkPool(ByteSpan region,const Layout & layout)28 ChunkPool::ChunkPool(ByteSpan region, const Layout& layout)
29 : Pool(kCapabilities, layout),
30 allocated_layout_(EnsurePointerLayout(layout)) {
31 Result<ByteSpan> result =
32 GetAlignedSubspan(region, allocated_layout_.alignment());
33 PW_CHECK_OK(result.status());
34 start_ = reinterpret_cast<uintptr_t>(region.data());
35 end_ = start_ + region.size() - (region.size() % allocated_layout_.size());
36 region = result.value();
37 next_ = region.data();
38 std::byte* current = next_;
39 std::byte* end = current + region.size();
40 std::byte** next = ¤t;
41 while (current < end) {
42 next = std::launder(reinterpret_cast<std::byte**>(current));
43 current += allocated_layout_.size();
44 *next = current;
45 }
46 *next = nullptr;
47 }
48
DoAllocate()49 void* ChunkPool::DoAllocate() {
50 if (next_ == nullptr) {
51 return nullptr;
52 }
53 std::byte* ptr = next_;
54 next_ = *(std::launder(reinterpret_cast<std::byte**>(next_)));
55 return ptr;
56 }
57
DoDeallocate(void * ptr)58 void ChunkPool::DoDeallocate(void* ptr) {
59 if (ptr == nullptr) {
60 return;
61 }
62 std::byte** next = std::launder(reinterpret_cast<std::byte**>(ptr));
63 *next = next_;
64 next_ = reinterpret_cast<std::byte*>(ptr);
65 }
66
DoGetInfo(InfoType info_type,const void * ptr) const67 Result<Layout> ChunkPool::DoGetInfo(InfoType info_type, const void* ptr) const {
68 if (info_type == InfoType::kCapacity) {
69 return Layout(end_ - start_, allocated_layout_.alignment());
70 }
71 auto addr = reinterpret_cast<uintptr_t>(ptr);
72 if (addr < start_ || end_ <= addr) {
73 return Status::OutOfRange();
74 }
75 if ((addr - start_) % allocated_layout_.size() != 0) {
76 return Status::OutOfRange();
77 }
78 switch (info_type) {
79 case InfoType::kRequestedLayoutOf:
80 case InfoType::kUsableLayoutOf:
81 case InfoType::kAllocatedLayoutOf:
82 return allocated_layout_;
83 case InfoType::kRecognizes:
84 return Layout();
85 case InfoType::kCapacity:
86 default:
87 return Status::Unimplemented();
88 }
89 }
90
91 } // namespace pw::allocator
92