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