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 <type_traits> 18 19 #include "pw_status/status.h" 20 21 namespace pw { 22 23 // StatusWithSize stores a status and an unsigned integer. The integer must not 24 // exceed StatusWithSize::max_size(), which is 134,217,727 (2**27 - 1) on 32-bit 25 // systems. 26 // 27 // StatusWithSize is useful for reporting the number of bytes read or written in 28 // an operation along with the status. For example, a function that writes a 29 // formatted string may want to report both the number of characters written and 30 // whether it ran out of space. 31 // 32 // StatusWithSize is more efficient than its alternatives. It packs a status and 33 // size into a single word, which can be returned from a function in a register. 34 // Because they are packed together, the size is limited to max_size(). 35 // 36 // StatusWithSize's alternatives result in larger code size. For example: 37 // 38 // 1. Return status, pass size output as a pointer argument. 39 // 40 // Requires an additional argument and forces the output argument to the 41 // stack in order to pass an address, increasing code size. 42 // 43 // 2. Return an object with Status and size members. 44 // 45 // At least for ARMv7-M, the returned struct is created on the stack, which 46 // increases code size. 47 // 48 class StatusWithSize { 49 public: 50 static constexpr StatusWithSize Cancelled(size_t size = 0) { 51 return StatusWithSize(Status::Cancelled(), size); 52 } 53 static constexpr StatusWithSize Unknown(size_t size = 0) { 54 return StatusWithSize(Status::Unknown(), size); 55 } 56 static constexpr StatusWithSize InvalidArgument(size_t size = 0) { 57 return StatusWithSize(Status::InvalidArgument(), size); 58 } 59 static constexpr StatusWithSize DeadlineExceeded(size_t size = 0) { 60 return StatusWithSize(Status::DeadlineExceeded(), size); 61 } 62 static constexpr StatusWithSize NotFound(size_t size = 0) { 63 return StatusWithSize(Status::NotFound(), size); 64 } 65 static constexpr StatusWithSize AlreadyExists(size_t size = 0) { 66 return StatusWithSize(Status::AlreadyExists(), size); 67 } 68 static constexpr StatusWithSize PermissionDenied(size_t size = 0) { 69 return StatusWithSize(Status::PermissionDenied(), size); 70 } 71 static constexpr StatusWithSize Unauthenticated(size_t size = 0) { 72 return StatusWithSize(Status::Unauthenticated(), size); 73 } 74 static constexpr StatusWithSize ResourceExhausted(size_t size = 0) { 75 return StatusWithSize(Status::ResourceExhausted(), size); 76 } 77 static constexpr StatusWithSize FailedPrecondition(size_t size = 0) { 78 return StatusWithSize(Status::FailedPrecondition(), size); 79 } 80 static constexpr StatusWithSize Aborted(size_t size = 0) { 81 return StatusWithSize(Status::Aborted(), size); 82 } 83 static constexpr StatusWithSize OutOfRange(size_t size = 0) { 84 return StatusWithSize(Status::OutOfRange(), size); 85 } 86 static constexpr StatusWithSize Unimplemented(size_t size = 0) { 87 return StatusWithSize(Status::Unimplemented(), size); 88 } 89 static constexpr StatusWithSize Internal(size_t size = 0) { 90 return StatusWithSize(Status::Internal(), size); 91 } 92 static constexpr StatusWithSize Unavailable(size_t size = 0) { 93 return StatusWithSize(Status::Unavailable(), size); 94 } 95 static constexpr StatusWithSize DataLoss(size_t size = 0) { 96 return StatusWithSize(Status::DataLoss(), size); 97 } 98 99 // Creates a StatusWithSize with OkStatus() and a size of 0. StatusWithSize()100 explicit constexpr StatusWithSize() : size_(0) {} 101 102 // Creates a StatusWithSize with status OK and the provided size. 103 // std::enable_if is used to prevent enum types (e.g. Status) from being used. 104 // TODO(hepler): Add debug-only assert that size <= max_size(). 105 template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>> StatusWithSize(T size)106 explicit constexpr StatusWithSize(T size) : size_(size) {} 107 108 // Creates a StatusWithSize with the provided status and size. StatusWithSize(Status status,size_t size)109 explicit constexpr StatusWithSize(Status status, size_t size) 110 : StatusWithSize((static_cast<size_t>(status.code()) << kStatusShift) | 111 size) {} 112 113 constexpr StatusWithSize(const StatusWithSize&) = default; 114 constexpr StatusWithSize& operator=(const StatusWithSize&) = default; 115 116 // Returns the size. The size is always present, even if status() is an error. size()117 constexpr size_t size() const { return size_ & kSizeMask; } 118 119 // The maximum valid value for size. max_size()120 static constexpr size_t max_size() { return kSizeMask; } 121 122 // True if status() == OkStatus(). ok()123 constexpr bool ok() const { return (size_ & kStatusMask) == 0u; } 124 status()125 constexpr Status status() const { 126 return static_cast<Status::Code>((size_ & kStatusMask) >> kStatusShift); 127 } 128 129 // Functions for checking which status the StatusWithSize contains. IsCancelled()130 [[nodiscard]] constexpr bool IsCancelled() const { 131 return status().IsCancelled(); 132 } IsUnknown()133 [[nodiscard]] constexpr bool IsUnknown() const { 134 return status().IsUnknown(); 135 } IsInvalidArgument()136 [[nodiscard]] constexpr bool IsInvalidArgument() const { 137 return status().IsInvalidArgument(); 138 } IsDeadlineExceeded()139 [[nodiscard]] constexpr bool IsDeadlineExceeded() const { 140 return status().IsDeadlineExceeded(); 141 } IsNotFound()142 [[nodiscard]] constexpr bool IsNotFound() const { 143 return status().IsNotFound(); 144 } IsAlreadyExists()145 [[nodiscard]] constexpr bool IsAlreadyExists() const { 146 return status().IsAlreadyExists(); 147 } IsPermissionDenied()148 [[nodiscard]] constexpr bool IsPermissionDenied() const { 149 return status().IsPermissionDenied(); 150 } IsResourceExhausted()151 [[nodiscard]] constexpr bool IsResourceExhausted() const { 152 return status().IsResourceExhausted(); 153 } IsFailedPrecondition()154 [[nodiscard]] constexpr bool IsFailedPrecondition() const { 155 return status().IsFailedPrecondition(); 156 } IsAborted()157 [[nodiscard]] constexpr bool IsAborted() const { 158 return status().IsAborted(); 159 } IsOutOfRange()160 [[nodiscard]] constexpr bool IsOutOfRange() const { 161 return status().IsOutOfRange(); 162 } IsUnimplemented()163 [[nodiscard]] constexpr bool IsUnimplemented() const { 164 return status().IsUnimplemented(); 165 } IsInternal()166 [[nodiscard]] constexpr bool IsInternal() const { 167 return status().IsInternal(); 168 } IsUnavailable()169 [[nodiscard]] constexpr bool IsUnavailable() const { 170 return status().IsUnavailable(); 171 } IsDataLoss()172 [[nodiscard]] constexpr bool IsDataLoss() const { 173 return status().IsDataLoss(); 174 } IsUnauthenticated()175 [[nodiscard]] constexpr bool IsUnauthenticated() const { 176 return status().IsUnauthenticated(); 177 } 178 179 private: 180 static constexpr size_t kStatusBits = 5; 181 static constexpr size_t kSizeMask = ~static_cast<size_t>(0) >> kStatusBits; 182 static constexpr size_t kStatusMask = ~kSizeMask; 183 static constexpr size_t kStatusShift = sizeof(size_t) * 8 - kStatusBits; 184 185 size_t size_; 186 }; 187 188 } // namespace pw 189