xref: /aosp_15_r20/external/pigweed/pw_allocator/block/public/pw_allocator/block/result.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 
19 #include "pw_allocator/config.h"
20 #include "pw_assert/assert.h"
21 #include "pw_status/status.h"
22 #include "pw_status/status_with_size.h"
23 
24 namespace pw::allocator {
25 namespace internal {
26 
27 /// Generic base class for `BlockResult`.
28 ///
29 /// This class communicates the results and side effects of allocator requests
30 /// by compactly combining a typical `Status` with enumerated values describing
31 /// how a block's previous and next neighboring blocks may have been changed.
32 class GenericBlockResult {
33  private:
34   static constexpr size_t kPrevBits = 5;
35   static constexpr size_t kPrevShift = 0;
36 
37   static constexpr size_t kNextBits = 5;
38   static constexpr size_t kNextShift = kPrevBits;
39 
40   static constexpr size_t kSizeBits = 10;
41   static constexpr size_t kSizeShift = kPrevBits + kNextBits;
42 
43  public:
44   enum class Prev : uint8_t {
45     kUnchanged,
46     kSplitNew,
47     kResizedSmaller,
48     kResizedLarger,
49   };
50 
51   enum class Next : uint8_t {
52     kUnchanged,
53     kSplitNew,
54     kResized,
55     kMerged,
56   };
57 
prev()58   [[nodiscard]] constexpr Prev prev() const {
59     return static_cast<Prev>(Decode(kPrevBits, kPrevShift));
60   }
61 
next()62   [[nodiscard]] constexpr Next next() const {
63     return static_cast<Next>(Decode(kNextBits, kNextShift));
64   }
65 
size()66   [[nodiscard]] constexpr size_t size() const {
67     return Decode(kSizeBits, kSizeShift);
68   }
69 
ok()70   [[nodiscard]] constexpr bool ok() const { return encoded_.ok(); }
71 
status()72   [[nodiscard]] constexpr Status status() const { return encoded_.status(); }
73 
74   /// Asserts the result is not an error if strict validation is enabled, and
75   /// does nothing otherwise.
IgnoreUnlessStrict()76   constexpr void IgnoreUnlessStrict() const {
77     if constexpr (PW_ALLOCATOR_STRICT_VALIDATION) {
78       PW_ASSERT(ok());
79     }
80   }
81 
82  protected:
GenericBlockResult(Status status,Prev prev,Next next,size_t size)83   constexpr GenericBlockResult(Status status, Prev prev, Next next, size_t size)
84       : encoded_(status,
85                  Encode(size_t(prev), kPrevBits, kPrevShift) |
86                      Encode(size_t(next), kNextBits, kNextShift) |
87                      Encode(size, kSizeBits, kSizeShift)) {}
88 
89  private:
Encode(size_t value,size_t bits,size_t shift)90   static constexpr size_t Encode(size_t value, size_t bits, size_t shift) {
91     if constexpr (PW_ALLOCATOR_STRICT_VALIDATION) {
92       PW_ASSERT(value < (1U << bits));
93     }
94     return value << shift;
95   }
96 
Decode(size_t bits,size_t shift)97   constexpr size_t Decode(size_t bits, size_t shift) const {
98     return (encoded_.size() >> shift) & ~(~static_cast<size_t>(0) << bits);
99   }
100 
101   StatusWithSize encoded_;
102 };
103 
104 }  // namespace internal
105 
106 /// Extends `GenericBlockResult` to include a pointer to a block.
107 ///
108 /// The included pointer is to the block affected by the operation that produced
109 /// a result. On error, this should be the original block. On success, it may be
110 /// a newly produced block.
111 ///
112 /// @tparam   BlockType   The type of the block included in the result.
113 template <typename BlockType>
114 class [[nodiscard]] BlockResult : public internal::GenericBlockResult {
115  public:
BlockResult(BlockType * block)116   constexpr explicit BlockResult(BlockType* block)
117       : BlockResult(block, OkStatus()) {}
118 
BlockResult(BlockType * block,Status status)119   constexpr BlockResult(BlockType* block, Status status)
120       : internal::GenericBlockResult(
121             status, Prev::kUnchanged, Next::kUnchanged, 0),
122         block_(block) {}
123 
BlockResult(BlockType * block,Prev prev)124   constexpr BlockResult(BlockType* block, Prev prev)
125       : BlockResult(block, prev, Next::kUnchanged, 0) {}
126 
BlockResult(BlockType * block,Prev prev,size_t shifted_to_prev)127   constexpr BlockResult(BlockType* block, Prev prev, size_t shifted_to_prev)
128       : BlockResult(block, prev, Next::kUnchanged, shifted_to_prev) {}
129 
BlockResult(BlockType * block,Next next)130   constexpr BlockResult(BlockType* block, Next next)
131       : BlockResult(block, Prev::kUnchanged, next, 0) {}
132 
BlockResult(BlockType * block,Prev prev,Next next)133   constexpr BlockResult(BlockType* block, Prev prev, Next next)
134       : BlockResult(block, prev, next, 0) {}
135 
BlockResult(BlockType * block,Prev prev,Next next,size_t shifted_to_prev)136   constexpr BlockResult(BlockType* block,
137                         Prev prev,
138                         Next next,
139                         size_t shifted_to_prev)
140       : internal::GenericBlockResult(OkStatus(), prev, next, shifted_to_prev),
141         block_(block) {
142     block->CheckInvariantsIfStrict();
143   }
144 
block()145   [[nodiscard]] constexpr BlockType* block() const { return block_; }
146 
147  private:
148   BlockType* block_ = nullptr;
149 };
150 
151 }  // namespace pw::allocator
152