Pigweed
 
Loading...
Searching...
No Matches
contiguous.h
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#pragma once
15
16#include "lib/stdcompat/bit.h"
17#include "pw_allocator/block/basic.h"
18#include "pw_allocator/hardening.h"
19#include "pw_bytes/span.h"
20
21namespace pw::allocator {
22namespace internal {
23
24// Trivial base class for trait support.
26
27} // namespace internal
28
44template <typename Derived>
46 protected:
47 constexpr explicit ContiguousBlock() {
48 // Assert within a function, since `Derived` is not complete when this type
49 // is defined.
50 static_assert(
51 is_block_v<Derived>,
52 "Types derived from ContiguousBlock must also derive from BasicBlock");
53 }
54
55 public:
58 constexpr Derived* Prev() const;
59
62 constexpr Derived* Next() const;
63
64 protected:
74 constexpr Derived* DoSplitFirst(size_t new_inner_size);
75
85 constexpr Derived* DoSplitLast(size_t new_inner_size);
86
93 constexpr void DoMergeNext();
94
96 constexpr bool DoCheckInvariants(bool strict) const;
97
98 private:
99 constexpr Derived* derived() { return static_cast<Derived*>(this); }
100 constexpr const Derived* derived() const {
101 return static_cast<const Derived*>(this);
102 }
103
105 constexpr Derived* PrevUnchecked() const;
106
108 constexpr Derived* NextUnchecked() const;
109
119 static constexpr Derived* Split(Derived*& block, size_t new_inner_size);
120
122 static constexpr ByteSpan AsBytes(Derived*&& block);
123
124 // PoisonableBlock calls DoSplitFirst, DoSplitLast, and DoMergeNext
125 template <typename>
126 friend class PoisonableBlock;
127};
128
130template <typename BlockType>
131struct is_contiguous : std::is_base_of<internal::ContiguousBase, BlockType> {};
132
134template <typename BlockType>
135constexpr bool is_contiguous_v = is_contiguous<BlockType>::value;
136
137namespace internal {
138
142
145void CheckNextMisaligned(const void* block,
146 const void* next,
147 bool next_is_aligned);
148
151void CheckNextPrevMismatched(const void* block,
152 const void* next,
153 const void* next_prev,
154 bool next_prev_matches);
155
158void CheckPrevMisaligned(const void* block,
159 const void* prev,
160 bool prev_is_aligned);
161
164void CheckPrevNextMismatched(const void* block,
165 const void* prev,
166 const void* prev_next,
167 bool prev_next_matches);
168
169} // namespace internal
170
171// Template method implementations.
172
173template <typename Derived>
174constexpr Derived* ContiguousBlock<Derived>::Prev() const {
175 if constexpr (Hardening::kIncludesDebugChecks) {
176 derived()->CheckInvariants();
177 }
178 return PrevUnchecked();
179}
180
181template <typename Derived>
182constexpr Derived* ContiguousBlock<Derived>::PrevUnchecked() const {
183 size_t prev_outer_size = derived()->PrevOuterSizeUnchecked();
184 if (prev_outer_size == 0) {
185 return nullptr;
186 }
187 auto addr = cpp20::bit_cast<uintptr_t>(this);
188 Hardening::Decrement(addr, prev_outer_size);
189 return std::launder(reinterpret_cast<Derived*>(addr));
190}
191
192template <typename Derived>
193constexpr Derived* ContiguousBlock<Derived>::Next() const {
194 if constexpr (Hardening::kIncludesDebugChecks) {
195 derived()->CheckInvariants();
196 }
197 return NextUnchecked();
198}
199
200template <typename Derived>
201constexpr Derived* ContiguousBlock<Derived>::NextUnchecked() const {
202 if (derived()->IsLastUnchecked()) {
203 return nullptr;
204 }
205 size_t outer_size = derived()->OuterSizeUnchecked();
206 auto addr = cpp20::bit_cast<uintptr_t>(this);
207 Hardening::Increment(addr, outer_size);
208 return std::launder(reinterpret_cast<Derived*>(addr));
209}
210
211template <typename Derived>
213 size_t new_inner_size) {
214 Derived* next = derived()->Next();
215 size_t new_outer_size = Derived::kBlockOverhead + new_inner_size;
216 ByteSpan bytes(derived()->UsableSpace(), derived()->InnerSize());
217 bytes = bytes.subspan(new_inner_size);
218 auto* trailing = Derived::AsBlock(bytes);
219 derived()->SetNext(new_outer_size, trailing);
220 trailing->SetNext(bytes.size(), next);
221 return trailing;
222}
223
224template <typename Derived>
226 size_t new_inner_size) {
227 size_t new_outer_size = Derived::kBlockOverhead + new_inner_size;
228 return DoSplitFirst(derived()->InnerSize() - new_outer_size);
229}
230
231template <typename Derived>
233 Derived* next = derived()->Next();
234 if (next != nullptr) {
235 size_t outer_size = derived()->OuterSize() + next->OuterSize();
236 derived()->SetNext(outer_size, next->Next());
237 }
238}
239
240template <typename Derived>
241constexpr bool ContiguousBlock<Derived>::DoCheckInvariants(bool strict) const {
242 bool valid = true;
243
244 Derived* next = derived()->NextUnchecked();
245 if (next != nullptr) {
246 valid &= (cpp20::bit_cast<uintptr_t>(next) % Derived::kAlignment) == 0;
247 if constexpr (Hardening::kIncludesDebugChecks) {
248 internal::CheckNextMisaligned(this, next, valid || !strict);
249 }
250
251 Derived* next_prev = next->PrevUnchecked();
252 valid &= this == next_prev;
253 if constexpr (Hardening::kIncludesDebugChecks) {
254 internal::CheckNextPrevMismatched(
255 this, next, next_prev, valid || !strict);
256 }
257 }
258
259 Derived* prev = derived()->PrevUnchecked();
260 if (prev != nullptr) {
261 valid &= (cpp20::bit_cast<uintptr_t>(prev) % Derived::kAlignment) == 0;
262 if constexpr (Hardening::kIncludesDebugChecks) {
263 internal::CheckPrevMisaligned(this, prev, valid || !strict);
264 }
265
266 Derived* prev_next = prev->NextUnchecked();
267 valid &= this == prev_next;
268 if constexpr (Hardening::kIncludesDebugChecks) {
269 internal::CheckPrevNextMismatched(
270 this, prev, prev_next, valid || !strict);
271 }
272 }
273
274 return valid;
275}
276
277} // namespace pw::allocator
Definition: contiguous.h:45
constexpr Derived * Prev() const
Definition: contiguous.h:174
constexpr bool DoCheckInvariants(bool strict) const
Performs the ContiguousBlock invariant checks.
Definition: contiguous.h:241
constexpr Derived * Next() const
Definition: contiguous.h:193
constexpr void DoMergeNext()
Definition: contiguous.h:232
constexpr Derived * DoSplitLast(size_t new_inner_size)
Definition: contiguous.h:225
constexpr Derived * DoSplitFirst(size_t new_inner_size)
Definition: contiguous.h:212
Definition: contiguous.h:25
Trait type that allow interrogating a block as to whether it is contiguous.
Definition: contiguous.h:131