Pigweed
 
Loading...
Searching...
No Matches
detailed_block.h
1// Copyright 2020 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 <limits>
19#include <new>
20
21#include "pw_allocator/block/alignable.h"
22#include "pw_allocator/block/allocatable.h"
23#include "pw_allocator/block/basic.h"
24#include "pw_allocator/block/contiguous.h"
25#include "pw_allocator/block/iterable.h"
26#include "pw_allocator/block/poisonable.h"
27#include "pw_allocator/block/result.h"
28#include "pw_allocator/block/with_layout.h"
29#include "pw_allocator/hardening.h"
30#include "pw_allocator/layout.h"
31#include "pw_assert/assert.h"
32#include "pw_bytes/span.h"
33#include "pw_status/status.h"
34
35namespace pw::allocator {
36
46template <typename OffsetType_, typename WhenFree>
48 using OffsetType = std::make_unsigned_t<OffsetType_>;
49 static constexpr Layout kLayoutWhenFree = Layout::Of<WhenFree>();
50};
51
52template <typename OffsetType_>
53struct DetailedBlockParameters<OffsetType_, void> {
54 using OffsetType = std::make_unsigned_t<OffsetType_>;
55 static constexpr Layout kLayoutWhenFree = Layout(0, 1);
56};
57
78template <typename Parameters>
80 : public BasicBlock<DetailedBlockImpl<Parameters>>,
81 public IterableBlock<DetailedBlockImpl<Parameters>>,
82 public ContiguousBlock<DetailedBlockImpl<Parameters>>,
83 public AllocatableBlock<DetailedBlockImpl<Parameters>>,
84 public AlignableBlock<DetailedBlockImpl<Parameters>>,
85 public BlockWithLayout<DetailedBlockImpl<Parameters>>,
86 public PoisonableBlock<DetailedBlockImpl<Parameters>> {
87 private:
90
91 public:
92 using OffsetType = typename Parameters::OffsetType;
93
94 using Range = typename IterableBlock<BlockType>::Range;
95 using Iterator = typename IterableBlock<BlockType>::Iterator;
96
97 private:
98 constexpr explicit DetailedBlockImpl(size_t outer_size) : info_{} {
99 next_ = outer_size / Basic::kAlignment;
100 info_.last = 1;
101 info_.alignment = Basic::kAlignment;
102 }
103
104 // `Basic` required methods.
105 friend Basic;
106 static constexpr size_t DefaultAlignment() {
107 return std::max(alignof(OffsetType),
108 Parameters::kLayoutWhenFree.alignment());
109 }
110 static constexpr size_t BlockOverhead() { return sizeof(BlockType); }
111 static constexpr size_t MinInnerSize() { return 1; }
112 static constexpr size_t ReservedWhenFree() {
113 return Parameters::kLayoutWhenFree.size();
114 }
115 constexpr size_t OuterSizeUnchecked() const;
116
117 // `Basic` overrides.
118 constexpr bool DoCheckInvariants(bool strict) const;
119
120 // `Contiguous` required methods.
122 friend Contiguous;
123
124 static constexpr size_t MaxAddressableSize() {
125 auto kOffsetMax =
126 static_cast<size_t>(std::numeric_limits<OffsetType>::max());
127 auto kSizeMax = static_cast<size_t>(std::numeric_limits<size_t>::max());
128 return std::min(kSizeMax / Basic::kAlignment, kOffsetMax) *
129 Basic::kAlignment;
130 }
131
132 constexpr bool IsLastUnchecked() const { return info_.last != 0; }
133 static constexpr BlockType* AsBlock(ByteSpan bytes);
134 constexpr void SetNext(size_t outer_size, BlockType* next);
135 constexpr size_t PrevOuterSizeUnchecked() const;
136
137 // `Contiguous` overrides.
138 constexpr BlockType* DoSplitFirst(size_t new_inner_size);
139 constexpr BlockType* DoSplitLast(size_t new_inner_size);
140 constexpr void DoMergeNext();
141
142 // `Allocatable` required methods.
144 friend Allocatable;
145 constexpr bool IsFreeUnchecked() const { return info_.used == 0; }
146 constexpr void SetFree(bool is_free);
147
148 // `Alignable` overrides.
150 friend Alignable;
151 constexpr StatusWithSize DoCanAlloc(Layout layout) const;
152 static constexpr BlockResult<BlockType> DoAllocFirst(BlockType*&& block,
153 Layout layout);
154 static constexpr BlockResult<BlockType> DoAllocLast(BlockType*&& block,
155 Layout layout);
156 constexpr BlockResult<BlockType> DoResize(size_t new_inner_size,
157 bool shifted = false);
158 static constexpr BlockResult<BlockType> DoFree(BlockType*&& block);
159
160 // `WithLayout` required methods.
162 friend WithLayout;
163 constexpr size_t RequestedSize() const {
164 return Basic::InnerSize() - padding_;
165 }
166 constexpr size_t RequestedAlignment() const { return info_.alignment; }
167 constexpr void SetRequestedSize(size_t size);
168 constexpr void SetRequestedAlignment(size_t alignment);
169
170 // `Poisonable` required methods.
172 friend Poisonable;
173 constexpr bool IsPoisonedUnchecked() const { return info_.poisoned != 0; }
174 constexpr void SetPoisoned(bool is_poisoned) { info_.poisoned = is_poisoned; }
175
178 OffsetType prev_ = 0;
179
183 OffsetType next_ = 0;
184
195 struct {
196 uint16_t used : 1;
197 uint16_t poisoned : 1;
198 uint16_t last : 1;
199 uint16_t alignment : 13;
200 } info_;
201
204 uint16_t padding_ = 0;
205};
206
207// Convenience alias that constructs the block traits automatically.
208template <typename OffsetType = uintptr_t, typename WhenFree = void>
209using DetailedBlock =
211
212// Template method implementations.
213
214// `Basic` methods.
215
216template <typename Parameters>
218 size_t outer_size = next_;
219 Hardening::Multiply(outer_size, Basic::kAlignment);
220 return outer_size;
221}
222
223template <typename Parameters>
224constexpr bool DetailedBlockImpl<Parameters>::DoCheckInvariants(
225 bool strict) const {
226 return Basic::DoCheckInvariants(strict) &&
227 Contiguous::DoCheckInvariants(strict) &&
228 Poisonable::DoCheckInvariants(strict);
229}
230
231// `Contiguous` methods.
232
233template <typename Parameters>
234constexpr DetailedBlockImpl<Parameters>* DetailedBlockImpl<Parameters>::AsBlock(
235 ByteSpan bytes) {
236 return ::new (bytes.data()) DetailedBlockImpl(bytes.size());
237}
238
239template <typename Parameters>
240constexpr void DetailedBlockImpl<Parameters>::SetNext(size_t outer_size,
241 BlockType* next) {
242 next_ = outer_size / Basic::kAlignment;
243 if (next == nullptr) {
244 info_.last = 1;
245 return;
246 }
247 info_.last = 0;
248 next->prev_ = next_;
249}
250
251template <typename Parameters>
252constexpr size_t DetailedBlockImpl<Parameters>::PrevOuterSizeUnchecked() const {
253 size_t outer_size = prev_;
254 Hardening::Multiply(outer_size, Basic::kAlignment);
255 return outer_size;
256}
257
258template <typename Parameters>
259constexpr DetailedBlockImpl<Parameters>*
260DetailedBlockImpl<Parameters>::DoSplitFirst(size_t new_inner_size) {
261 return Poisonable::DoSplitFirst(new_inner_size);
262}
263
264template <typename Parameters>
265constexpr DetailedBlockImpl<Parameters>*
266DetailedBlockImpl<Parameters>::DoSplitLast(size_t new_inner_size) {
267 return Poisonable::DoSplitLast(new_inner_size);
268}
269
270template <typename Parameters>
271constexpr void DetailedBlockImpl<Parameters>::DoMergeNext() {
272 Poisonable::DoMergeNext();
273}
274
275// `Alignable` methods.
276
277template <typename Parameters>
278constexpr void DetailedBlockImpl<Parameters>::SetFree(bool is_free) {
279 info_.used = !is_free;
280 Poisonable::SetFree(is_free);
281}
282
283// `Alignable` methods.
284
285template <typename Parameters>
286constexpr StatusWithSize DetailedBlockImpl<Parameters>::DoCanAlloc(
287 Layout layout) const {
288 return Alignable::DoCanAlloc(layout);
289}
290
291template <typename Parameters>
292constexpr BlockResult<DetailedBlockImpl<Parameters>>
293DetailedBlockImpl<Parameters>::DoAllocFirst(DetailedBlockImpl*&& block,
294 Layout layout) {
295 return WithLayout::DoAllocFirst(std::move(block), layout);
296}
297
298template <typename Parameters>
299constexpr BlockResult<DetailedBlockImpl<Parameters>>
300DetailedBlockImpl<Parameters>::DoAllocLast(DetailedBlockImpl*&& block,
301 Layout layout) {
302 return WithLayout::DoAllocLast(std::move(block), layout);
303}
304
305template <typename Parameters>
306constexpr BlockResult<DetailedBlockImpl<Parameters>>
307DetailedBlockImpl<Parameters>::DoResize(size_t new_inner_size, bool shifted) {
308 return WithLayout::DoResize(new_inner_size, shifted);
309}
310
311template <typename Parameters>
312constexpr BlockResult<DetailedBlockImpl<Parameters>>
313DetailedBlockImpl<Parameters>::DoFree(DetailedBlockImpl*&& block) {
314 return WithLayout::DoFree(std::move(block));
315}
316
317// `WithLayout` methods.
318
319template <typename Parameters>
320constexpr void DetailedBlockImpl<Parameters>::SetRequestedSize(size_t size) {
321 size_t inner_size = Basic::InnerSize();
322 size_t padding = inner_size;
323 Hardening::Decrement(padding, size);
324 if constexpr (Hardening::kIncludesDebugChecks) {
325 PW_ASSERT(padding <= std::numeric_limits<uint16_t>::max());
326 }
327 padding_ = static_cast<uint16_t>(padding);
328}
329
330template <typename Parameters>
331constexpr void DetailedBlockImpl<Parameters>::SetRequestedAlignment(
332 size_t alignment) {
333 if constexpr (Hardening::kIncludesDebugChecks) {
334 PW_ASSERT((alignment & (alignment - 1)) == 0);
335 PW_ASSERT(alignment < 0x2000);
336 }
337 info_.alignment = alignment;
338}
339
340} // namespace pw::allocator
Definition: status_with_size.h:49
Definition: alignable.h:42
Definition: allocatable.h:49
Definition: basic.h:93
constexpr size_t InnerSize() const
Definition: basic.h:284
Definition: result.h:114
Definition: with_layout.h:48
Definition: contiguous.h:45
Definition: detailed_block.h:86
Definition: iterable.h:74
Definition: iterable.h:33
Definition: layout.h:56
Definition: poisonable.h:51
Definition: detailed_block.h:47