xref: /aosp_15_r20/external/pigweed/pw_status/public/pw_status/status_with_size.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 <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
24 /// not exceed `StatusWithSize::max_size()`, which is 134,217,727 (2**27 - 1) on
25 /// 32-bit systems.
26 ///
27 /// `StatusWithSize` is useful for reporting the number of bytes read or written
28 /// in 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
30 /// and whether it ran out of space.
31 ///
32 /// `StatusWithSize` is more efficient than its alternatives. It packs a status
33 /// and size into a single word, which can be returned from a function in a
34 /// register. Because they are packed together, the size is limited to
35 /// max_size().
36 ///
37 /// `StatusWithSize`'s alternatives result in larger code size. For example:
38 ///
39 /// 1. Return status, pass size output as a pointer argument.
40 ///
41 ///    Requires an additional argument and forces the output argument to the
42 ///    stack in order to pass an address, increasing code size.
43 ///
44 /// 2. Return an object with Status and size members.
45 ///
46 ///    At least for ARMv7-M, the returned struct is created on the stack,
47 ///    which increases code size.
48 ///
49 class _PW_STATUS_NO_DISCARD StatusWithSize {
50  public:
51   static constexpr StatusWithSize Cancelled(size_t size = 0) {
52     return StatusWithSize(Status::Cancelled(), size);
53   }
54   static constexpr StatusWithSize Unknown(size_t size = 0) {
55     return StatusWithSize(Status::Unknown(), size);
56   }
57   static constexpr StatusWithSize InvalidArgument(size_t size = 0) {
58     return StatusWithSize(Status::InvalidArgument(), size);
59   }
60   static constexpr StatusWithSize DeadlineExceeded(size_t size = 0) {
61     return StatusWithSize(Status::DeadlineExceeded(), size);
62   }
63   static constexpr StatusWithSize NotFound(size_t size = 0) {
64     return StatusWithSize(Status::NotFound(), size);
65   }
66   static constexpr StatusWithSize AlreadyExists(size_t size = 0) {
67     return StatusWithSize(Status::AlreadyExists(), size);
68   }
69   static constexpr StatusWithSize PermissionDenied(size_t size = 0) {
70     return StatusWithSize(Status::PermissionDenied(), size);
71   }
72   static constexpr StatusWithSize Unauthenticated(size_t size = 0) {
73     return StatusWithSize(Status::Unauthenticated(), size);
74   }
75   static constexpr StatusWithSize ResourceExhausted(size_t size = 0) {
76     return StatusWithSize(Status::ResourceExhausted(), size);
77   }
78   static constexpr StatusWithSize FailedPrecondition(size_t size = 0) {
79     return StatusWithSize(Status::FailedPrecondition(), size);
80   }
81   static constexpr StatusWithSize Aborted(size_t size = 0) {
82     return StatusWithSize(Status::Aborted(), size);
83   }
84   static constexpr StatusWithSize OutOfRange(size_t size = 0) {
85     return StatusWithSize(Status::OutOfRange(), size);
86   }
87   static constexpr StatusWithSize Unimplemented(size_t size = 0) {
88     return StatusWithSize(Status::Unimplemented(), size);
89   }
90   static constexpr StatusWithSize Internal(size_t size = 0) {
91     return StatusWithSize(Status::Internal(), size);
92   }
93   static constexpr StatusWithSize Unavailable(size_t size = 0) {
94     return StatusWithSize(Status::Unavailable(), size);
95   }
96   static constexpr StatusWithSize DataLoss(size_t size = 0) {
97     return StatusWithSize(Status::DataLoss(), size);
98   }
99 
100   /// Creates a `StatusWithSize` with status `OK` and a size of 0.
StatusWithSize()101   explicit constexpr StatusWithSize() : size_(0) {}
102 
103   /// Creates a `StatusWithSize` with status `OK` and the provided size.
104   /// `std::enable_if` is used to prevent enum types (e.g. `Status::Code`) from
105   /// being used.
106   template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
StatusWithSize(T size)107   explicit constexpr StatusWithSize(T size) : size_(static_cast<size_t>(size)) {
108     // TODO(hepler): Add debug-only assert that size <= max_size().
109   }
110 
111   /// Creates a StatusWithSize with the provided status and size.
StatusWithSize(Status status,size_t size)112   explicit constexpr StatusWithSize(Status status, size_t size)
113       : StatusWithSize((static_cast<size_t>(status.code()) << kStatusShift) |
114                        size) {}
115 
116   constexpr StatusWithSize(const StatusWithSize&) = default;
117   constexpr StatusWithSize& operator=(const StatusWithSize&) = default;
118 
119   /// `Update` s this status and adds to `size`.
120   ///
121   /// The resulting `StatusWithSize` will have a size of `this->size() +
122   /// new_status_with_size.size()`
123   ///
124   /// The resulting status will be `OK` if both statuses are `OK`, otherwise it
125   /// will take on the earliest non-`OK` status.
UpdateAndAdd(StatusWithSize new_status_with_size)126   constexpr void UpdateAndAdd(StatusWithSize new_status_with_size) {
127     Status new_status;
128     if (ok()) {
129       new_status = new_status_with_size.status();
130     } else {
131       new_status = status();
132     }
133     size_t new_size = size() + new_status_with_size.size();
134     *this = StatusWithSize(new_status, new_size);
135   }
136 
137   /// Zeroes the size if the status is not `OK`.
ZeroIfNotOk()138   constexpr void ZeroIfNotOk() {
139     if (!ok()) {
140       *this = StatusWithSize(status(), 0);
141     }
142   }
143 
144   /// Returns the size. The size is always present, even if `status()` is an
145   /// error.
size()146   [[nodiscard]] constexpr size_t size() const { return size_ & kSizeMask; }
147 
148   /// The maximum valid value for size.
max_size()149   [[nodiscard]] static constexpr size_t max_size() { return kSizeMask; }
150 
151   /// True if status() == OkStatus().
ok()152   [[nodiscard]] constexpr bool ok() const {
153     return (size_ & kStatusMask) == 0u;
154   }
155 
156   /// Ignores any errors. This method does nothing except potentially suppress
157   /// complaints from any tools that are checking that errors are not dropped on
158   /// the floor.
IgnoreError()159   constexpr void IgnoreError() const {}
160 
status()161   [[nodiscard]] constexpr Status status() const {
162     return static_cast<Status::Code>((size_ & kStatusMask) >> kStatusShift);
163   }
164 
165   // Functions for checking which status the StatusWithSize contains.
IsCancelled()166   [[nodiscard]] constexpr bool IsCancelled() const {
167     return status().IsCancelled();
168   }
IsUnknown()169   [[nodiscard]] constexpr bool IsUnknown() const {
170     return status().IsUnknown();
171   }
IsInvalidArgument()172   [[nodiscard]] constexpr bool IsInvalidArgument() const {
173     return status().IsInvalidArgument();
174   }
IsDeadlineExceeded()175   [[nodiscard]] constexpr bool IsDeadlineExceeded() const {
176     return status().IsDeadlineExceeded();
177   }
IsNotFound()178   [[nodiscard]] constexpr bool IsNotFound() const {
179     return status().IsNotFound();
180   }
IsAlreadyExists()181   [[nodiscard]] constexpr bool IsAlreadyExists() const {
182     return status().IsAlreadyExists();
183   }
IsPermissionDenied()184   [[nodiscard]] constexpr bool IsPermissionDenied() const {
185     return status().IsPermissionDenied();
186   }
IsResourceExhausted()187   [[nodiscard]] constexpr bool IsResourceExhausted() const {
188     return status().IsResourceExhausted();
189   }
IsFailedPrecondition()190   [[nodiscard]] constexpr bool IsFailedPrecondition() const {
191     return status().IsFailedPrecondition();
192   }
IsAborted()193   [[nodiscard]] constexpr bool IsAborted() const {
194     return status().IsAborted();
195   }
IsOutOfRange()196   [[nodiscard]] constexpr bool IsOutOfRange() const {
197     return status().IsOutOfRange();
198   }
IsUnimplemented()199   [[nodiscard]] constexpr bool IsUnimplemented() const {
200     return status().IsUnimplemented();
201   }
IsInternal()202   [[nodiscard]] constexpr bool IsInternal() const {
203     return status().IsInternal();
204   }
IsUnavailable()205   [[nodiscard]] constexpr bool IsUnavailable() const {
206     return status().IsUnavailable();
207   }
IsDataLoss()208   [[nodiscard]] constexpr bool IsDataLoss() const {
209     return status().IsDataLoss();
210   }
IsUnauthenticated()211   [[nodiscard]] constexpr bool IsUnauthenticated() const {
212     return status().IsUnauthenticated();
213   }
214 
215  private:
216   static constexpr size_t kStatusBits = 5;
217   static constexpr size_t kSizeMask = ~static_cast<size_t>(0) >> kStatusBits;
218   static constexpr size_t kStatusMask = ~kSizeMask;
219   static constexpr size_t kStatusShift = sizeof(size_t) * 8 - kStatusBits;
220 
221   size_t size_;
222 };
223 
224 namespace internal {
225 
ConvertToStatus(StatusWithSize status_with_size)226 constexpr Status ConvertToStatus(StatusWithSize status_with_size) {
227   return status_with_size.status();
228 }
229 
ConvertToValue(StatusWithSize status_with_size)230 constexpr size_t ConvertToValue(StatusWithSize status_with_size) {
231   return status_with_size.size();
232 }
233 
ConvertToStatusWithSize(Status status)234 constexpr StatusWithSize ConvertToStatusWithSize(Status status) {
235   return StatusWithSize(status, 0);
236 }
237 
ConvertToStatusWithSize(StatusWithSize status_with_size)238 constexpr StatusWithSize ConvertToStatusWithSize(
239     StatusWithSize status_with_size) {
240   return status_with_size;
241 }
242 
243 }  // namespace internal
244 }  // namespace pw
245