• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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