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 _PW_STATUS_NO_DISCARD 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) 107 : size_(static_cast<size_t>(size)) {} 108 109 // Creates a StatusWithSize with the provided status and size. StatusWithSize(Status status,size_t size)110 explicit constexpr StatusWithSize(Status status, size_t size) 111 : StatusWithSize((static_cast<size_t>(status.code()) << kStatusShift) | 112 size) {} 113 114 constexpr StatusWithSize(const StatusWithSize&) = default; 115 constexpr StatusWithSize& operator=(const StatusWithSize&) = default; 116 117 /// ``Update`` s this status and adds to ``size``. 118 /// 119 /// The resulting ``StatusWithSize`` will have a size of 120 /// ``this->size() + new_status_with_size.size()`` 121 /// 122 /// The resulting status will be Ok if both statuses are ``Ok``, 123 /// otherwise it will take on the earliest non-``Ok`` status. UpdateAndAdd(StatusWithSize new_status_with_size)124 constexpr void UpdateAndAdd(StatusWithSize new_status_with_size) { 125 Status new_status; 126 if (ok()) { 127 new_status = new_status_with_size.status(); 128 } else { 129 new_status = status(); 130 } 131 size_t new_size = size() + new_status_with_size.size(); 132 *this = StatusWithSize(new_status, new_size); 133 } 134 135 /// Zeros this size if the status is not ``Ok``. ZeroIfNotOk()136 constexpr void ZeroIfNotOk() { 137 if (!ok()) { 138 *this = StatusWithSize(status(), 0); 139 } 140 } 141 142 // Returns the size. The size is always present, even if status() is an error. size()143 [[nodiscard]] constexpr size_t size() const { return size_ & kSizeMask; } 144 145 // Returns the size if the status() == OkStatus(), or the given default value 146 // if status() is an error. size_or(size_t default_value)147 [[nodiscard]] constexpr size_t size_or(size_t default_value) { 148 return ok() ? size() : default_value; 149 } 150 151 // The maximum valid value for size. max_size()152 [[nodiscard]] static constexpr size_t max_size() { return kSizeMask; } 153 154 // True if status() == OkStatus(). ok()155 [[nodiscard]] constexpr bool ok() const { 156 return (size_ & kStatusMask) == 0u; 157 } 158 159 // Ignores any errors. This method does nothing except potentially suppress 160 // complaints from any tools that are checking that errors are not dropped on 161 // the floor. IgnoreError()162 constexpr void IgnoreError() const {} 163 status()164 [[nodiscard]] constexpr Status status() const { 165 return static_cast<Status::Code>((size_ & kStatusMask) >> kStatusShift); 166 } 167 168 // Functions for checking which status the StatusWithSize contains. IsCancelled()169 [[nodiscard]] constexpr bool IsCancelled() const { 170 return status().IsCancelled(); 171 } IsUnknown()172 [[nodiscard]] constexpr bool IsUnknown() const { 173 return status().IsUnknown(); 174 } IsInvalidArgument()175 [[nodiscard]] constexpr bool IsInvalidArgument() const { 176 return status().IsInvalidArgument(); 177 } IsDeadlineExceeded()178 [[nodiscard]] constexpr bool IsDeadlineExceeded() const { 179 return status().IsDeadlineExceeded(); 180 } IsNotFound()181 [[nodiscard]] constexpr bool IsNotFound() const { 182 return status().IsNotFound(); 183 } IsAlreadyExists()184 [[nodiscard]] constexpr bool IsAlreadyExists() const { 185 return status().IsAlreadyExists(); 186 } IsPermissionDenied()187 [[nodiscard]] constexpr bool IsPermissionDenied() const { 188 return status().IsPermissionDenied(); 189 } IsResourceExhausted()190 [[nodiscard]] constexpr bool IsResourceExhausted() const { 191 return status().IsResourceExhausted(); 192 } IsFailedPrecondition()193 [[nodiscard]] constexpr bool IsFailedPrecondition() const { 194 return status().IsFailedPrecondition(); 195 } IsAborted()196 [[nodiscard]] constexpr bool IsAborted() const { 197 return status().IsAborted(); 198 } IsOutOfRange()199 [[nodiscard]] constexpr bool IsOutOfRange() const { 200 return status().IsOutOfRange(); 201 } IsUnimplemented()202 [[nodiscard]] constexpr bool IsUnimplemented() const { 203 return status().IsUnimplemented(); 204 } IsInternal()205 [[nodiscard]] constexpr bool IsInternal() const { 206 return status().IsInternal(); 207 } IsUnavailable()208 [[nodiscard]] constexpr bool IsUnavailable() const { 209 return status().IsUnavailable(); 210 } IsDataLoss()211 [[nodiscard]] constexpr bool IsDataLoss() const { 212 return status().IsDataLoss(); 213 } IsUnauthenticated()214 [[nodiscard]] constexpr bool IsUnauthenticated() const { 215 return status().IsUnauthenticated(); 216 } 217 218 private: 219 static constexpr size_t kStatusBits = 5; 220 static constexpr size_t kSizeMask = ~static_cast<size_t>(0) >> kStatusBits; 221 static constexpr size_t kStatusMask = ~kSizeMask; 222 static constexpr size_t kStatusShift = sizeof(size_t) * 8 - kStatusBits; 223 224 size_t size_; 225 }; 226 227 } // namespace pw 228