Pigweed
 
Loading...
Searching...
No Matches
basic.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 <cstddef>
17#include <cstdint>
18#include <new>
19#include <type_traits>
20
21#include "lib/stdcompat/bit.h"
22#include "pw_allocator/hardening.h"
23#include "pw_bytes/alignment.h"
24#include "pw_result/result.h"
25#include "pw_status/status.h"
26
27namespace pw::allocator {
28namespace internal {
29
31template <typename From, typename To>
32struct CopyConst {
33 using type = std::conditional_t<std::is_const_v<From>,
34 std::add_const_t<To>,
35 std::remove_const_t<To>>;
36};
37
38template <typename From, typename To>
39using copy_const_t = typename CopyConst<From, To>::type;
40
41template <typename From, typename To>
42using copy_const_ptr_t =
43 std::add_pointer_t<typename CopyConst<std::remove_pointer_t<From>,
44 std::remove_pointer_t<To>>::type>;
45
46// Trivial base class for trait support.
47struct BasicBase {};
48
49} // namespace internal
50
92template <typename Derived>
94 public:
95 static constexpr size_t kAlignment = Derived::DefaultAlignment();
96 static constexpr size_t kBlockOverhead =
97 AlignUp(Derived::BlockOverhead(), kAlignment);
98 static constexpr size_t kMinOuterSize =
99 kBlockOverhead + AlignUp(Derived::MinInnerSize(), kAlignment);
100
101 ~BasicBlock() = default;
102
103 // No copy or move.
104 BasicBlock(const BasicBlock& other) = delete;
105 BasicBlock& operator=(const BasicBlock& other) = delete;
106
122 static constexpr Result<Derived*> Init(ByteSpan region);
123
131 template <typename Ptr>
132 static constexpr internal::copy_const_ptr_t<Ptr, Derived*> FromUsableSpace(
133 Ptr usable_space);
134
136 constexpr std::byte* UsableSpace();
137 constexpr const std::byte* UsableSpace() const;
138 constexpr std::byte* UsableSpaceUnchecked() {
139 return UsableSpaceUncheckedImpl(this);
140 }
141 constexpr const std::byte* UsableSpaceUnchecked() const {
142 return UsableSpaceUncheckedImpl(this);
143 }
144
146 static constexpr size_t OuterSizeFromInnerSize(size_t inner_size);
147
149 static constexpr size_t InnerSizeFromOuterSize(size_t outer_size);
150
152 constexpr size_t OuterSize() const;
153
155 constexpr size_t InnerSize() const;
156 constexpr size_t InnerSizeUnchecked() const;
157
159 constexpr bool IsValid() const;
160
162 constexpr bool CheckInvariants() const;
163
164 protected:
165 constexpr BasicBlock() = default;
166
171 constexpr bool DoCheckInvariants(bool strict) const;
172
173 private:
174 constexpr const Derived* derived() const {
175 return static_cast<const Derived*>(this);
176 }
177
179 template <typename Ptr>
180 static constexpr internal::copy_const_ptr_t<Ptr, std::byte*>
181 UsableSpaceUncheckedImpl(Ptr block);
182};
183
186template <typename T>
187struct is_block : std::is_base_of<internal::BasicBase, T> {};
188
190template <typename T>
191constexpr bool is_block_v = is_block<T>::value;
192
193namespace internal {
194
197void CheckMisaligned(const void* block, bool is_aligned);
198
199} // namespace internal
200
201// Template method implementations.
202
203template <typename Derived>
204constexpr Result<Derived*> BasicBlock<Derived>::Init(ByteSpan region) {
205 region = GetAlignedSubspan(region, Derived::kAlignment);
206 if (region.size() <= Derived::kBlockOverhead) {
207 return Status::ResourceExhausted();
208 }
209 if (region.size() > Derived::MaxAddressableSize()) {
210 return Status::OutOfRange();
211 }
212 auto* block = Derived::AsBlock(region);
213 if constexpr (Hardening::kIncludesDebugChecks) {
214 block->CheckInvariants();
215 }
216 return block;
217}
218
219template <typename Derived>
220template <typename Ptr>
221constexpr internal::copy_const_ptr_t<Ptr, Derived*>
223 using BlockPtr = internal::copy_const_ptr_t<Ptr, Derived*>;
224 auto addr = cpp20::bit_cast<uintptr_t>(usable_space);
225 Hardening::Decrement(addr, kBlockOverhead);
226 auto* block = std::launder(reinterpret_cast<BlockPtr>(addr));
227 if constexpr (Hardening::kIncludesBasicChecks) {
228 block->CheckInvariants();
229 }
230 return block;
231}
232
233template <typename Derived>
234constexpr std::byte* BasicBlock<Derived>::UsableSpace() {
235 if constexpr (Hardening::kIncludesDebugChecks) {
236 CheckInvariants();
237 }
238 return UsableSpaceUnchecked();
239}
240
241template <typename Derived>
242constexpr const std::byte* BasicBlock<Derived>::UsableSpace() const {
243 if constexpr (Hardening::kIncludesDebugChecks) {
244 CheckInvariants();
245 }
246 return UsableSpaceUnchecked();
247}
248
249template <typename Derived>
250template <typename Ptr>
251constexpr internal::copy_const_ptr_t<Ptr, std::byte*>
252BasicBlock<Derived>::UsableSpaceUncheckedImpl(Ptr block) {
253 using BytePtr = internal::copy_const_ptr_t<Derived, std::byte*>;
254 auto addr = cpp20::bit_cast<uintptr_t>(block);
255 Hardening::Increment(addr, kBlockOverhead);
256 return cpp20::bit_cast<BytePtr>(addr);
257}
258
259template <typename Derived>
261 size_t inner_size) {
262 size_t outer_size = inner_size;
263 Hardening::Increment(outer_size, kBlockOverhead);
264 return outer_size;
265}
266
267template <typename Derived>
269 size_t outer_size) {
270 size_t inner_size = outer_size;
271 Hardening::Decrement(inner_size, kBlockOverhead);
272 return inner_size;
273}
274
275template <typename Derived>
276constexpr size_t BasicBlock<Derived>::OuterSize() const {
277 if constexpr (Hardening::kIncludesDebugChecks) {
278 CheckInvariants();
279 }
280 return derived()->OuterSizeUnchecked();
281}
282
283template <typename Derived>
284constexpr size_t BasicBlock<Derived>::InnerSize() const {
285 if constexpr (Hardening::kIncludesDebugChecks) {
286 CheckInvariants();
287 }
288 return InnerSizeUnchecked();
289}
290
291template <typename Derived>
292constexpr size_t BasicBlock<Derived>::InnerSizeUnchecked() const {
293 return InnerSizeFromOuterSize(derived()->OuterSizeUnchecked());
294}
295
296template <typename Derived>
297constexpr bool BasicBlock<Derived>::IsValid() const {
298 return derived()->DoCheckInvariants(/*strict=*/false);
299}
300
301template <typename Derived>
303 return derived()->DoCheckInvariants(/*strict=*/true);
304}
305
306template <typename Derived>
307constexpr bool BasicBlock<Derived>::DoCheckInvariants(bool strict) const {
308 bool is_aligned = (cpp20::bit_cast<uintptr_t>(this) % kAlignment) == 0;
309 if constexpr (Hardening::kIncludesDebugChecks) {
310 internal::CheckMisaligned(this, is_aligned || !strict);
311 }
312 return is_aligned;
313}
314
315} // namespace pw::allocator
Definition: basic.h:93
constexpr bool CheckInvariants() const
Like IsValid, but crashes if invalid.
Definition: basic.h:302
constexpr size_t OuterSize() const
Definition: basic.h:276
static constexpr Result< Derived * > Init(ByteSpan region)
Creates the first block for a given memory region.
Definition: basic.h:204
constexpr size_t InnerSize() const
Definition: basic.h:284
static constexpr size_t OuterSizeFromInnerSize(size_t inner_size)
Definition: basic.h:260
constexpr bool IsValid() const
Definition: basic.h:297
static constexpr size_t InnerSizeFromOuterSize(size_t outer_size)
Definition: basic.h:268
constexpr bool DoCheckInvariants(bool strict) const
Definition: basic.h:307
constexpr std::byte * UsableSpace()
Definition: basic.h:234
static constexpr internal::copy_const_ptr_t< Ptr, Derived * > FromUsableSpace(Ptr usable_space)
Definition: basic.h:222
ByteSpan GetAlignedSubspan(ByteSpan bytes, size_t alignment)
constexpr size_t AlignUp(uintptr_t value, size_t alignment)
Returns the value rounded up to the nearest multiple of alignment.
Definition: alignment.h:52
Helper type that copies const-qualifers between types.
Definition: basic.h:32
Definition: basic.h:187