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