1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
17 #include <type_traits>
19 #include "pw_status/status.h"
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
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.
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().
36 // StatusWithSize's alternatives result in larger code size. For example:
38 // 1. Return status, pass size output as a pointer argument.
40 // Requires an additional argument and forces the output argument to the
41 // stack in order to pass an address, increasing code size.
43 // 2. Return an object with Status and size members.
45 // At least for ARMv7-M, the returned struct is created on the stack, which
46 // increases code size.
48 class StatusWithSize {
50 static constexpr StatusWithSize Cancelled(size_t size = 0) {
51 return StatusWithSize(Status::Cancelled(), size);
53 static constexpr StatusWithSize Unknown(size_t size = 0) {
54 return StatusWithSize(Status::Unknown(), size);
56 static constexpr StatusWithSize InvalidArgument(size_t size = 0) {
57 return StatusWithSize(Status::InvalidArgument(), size);
59 static constexpr StatusWithSize DeadlineExceeded(size_t size = 0) {
60 return StatusWithSize(Status::DeadlineExceeded(), size);
62 static constexpr StatusWithSize NotFound(size_t size = 0) {
63 return StatusWithSize(Status::NotFound(), size);
65 static constexpr StatusWithSize AlreadyExists(size_t size = 0) {
66 return StatusWithSize(Status::AlreadyExists(), size);
68 static constexpr StatusWithSize PermissionDenied(size_t size = 0) {
69 return StatusWithSize(Status::PermissionDenied(), size);
71 static constexpr StatusWithSize Unauthenticated(size_t size = 0) {
72 return StatusWithSize(Status::Unauthenticated(), size);
74 static constexpr StatusWithSize ResourceExhausted(size_t size = 0) {
75 return StatusWithSize(Status::ResourceExhausted(), size);
77 static constexpr StatusWithSize FailedPrecondition(size_t size = 0) {
78 return StatusWithSize(Status::FailedPrecondition(), size);
80 static constexpr StatusWithSize Aborted(size_t size = 0) {
81 return StatusWithSize(Status::Aborted(), size);
83 static constexpr StatusWithSize OutOfRange(size_t size = 0) {
84 return StatusWithSize(Status::OutOfRange(), size);
86 static constexpr StatusWithSize Unimplemented(size_t size = 0) {
87 return StatusWithSize(Status::Unimplemented(), size);
89 static constexpr StatusWithSize Internal(size_t size = 0) {
90 return StatusWithSize(Status::Internal(), size);
92 static constexpr StatusWithSize Unavailable(size_t size = 0) {
93 return StatusWithSize(Status::Unavailable(), size);
95 static constexpr StatusWithSize DataLoss(size_t size = 0) {
96 return StatusWithSize(Status::DataLoss(), size);
99 // Creates a StatusWithSize with OkStatus() and a size of 0.
100 explicit constexpr StatusWithSize() : size_(0) {}
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>>
106 explicit constexpr StatusWithSize(T size) : size_(size) {}
108 // Creates a StatusWithSize with the provided status and size.
109 explicit constexpr StatusWithSize(Status status, size_t size)
110 : StatusWithSize((static_cast<size_t>(status.code()) << kStatusShift) |
113 constexpr StatusWithSize(const StatusWithSize&) = default;
114 constexpr StatusWithSize& operator=(const StatusWithSize&) = default;
116 // Returns the size. The size is always present, even if status() is an error.
117 constexpr size_t size() const { return size_ & kSizeMask; }
119 // The maximum valid value for size.
120 static constexpr size_t max_size() { return kSizeMask; }
122 // True if status() == OkStatus().
123 constexpr bool ok() const { return (size_ & kStatusMask) == 0u; }
125 constexpr Status status() const {
126 return static_cast<Status::Code>((size_ & kStatusMask) >> kStatusShift);
129 // Functions for checking which status the StatusWithSize contains.
130 [[nodiscard]] constexpr bool IsCancelled() const {
131 return status().IsCancelled();
133 [[nodiscard]] constexpr bool IsUnknown() const {
134 return status().IsUnknown();
136 [[nodiscard]] constexpr bool IsInvalidArgument() const {
137 return status().IsInvalidArgument();
139 [[nodiscard]] constexpr bool IsDeadlineExceeded() const {
140 return status().IsDeadlineExceeded();
142 [[nodiscard]] constexpr bool IsNotFound() const {
143 return status().IsNotFound();
145 [[nodiscard]] constexpr bool IsAlreadyExists() const {
146 return status().IsAlreadyExists();
148 [[nodiscard]] constexpr bool IsPermissionDenied() const {
149 return status().IsPermissionDenied();
151 [[nodiscard]] constexpr bool IsResourceExhausted() const {
152 return status().IsResourceExhausted();
154 [[nodiscard]] constexpr bool IsFailedPrecondition() const {
155 return status().IsFailedPrecondition();
157 [[nodiscard]] constexpr bool IsAborted() const {
158 return status().IsAborted();
160 [[nodiscard]] constexpr bool IsOutOfRange() const {
161 return status().IsOutOfRange();
163 [[nodiscard]] constexpr bool IsUnimplemented() const {
164 return status().IsUnimplemented();
166 [[nodiscard]] constexpr bool IsInternal() const {
167 return status().IsInternal();
169 [[nodiscard]] constexpr bool IsUnavailable() const {
170 return status().IsUnavailable();
172 [[nodiscard]] constexpr bool IsDataLoss() const {
173 return status().IsDataLoss();
175 [[nodiscard]] constexpr bool IsUnauthenticated() const {
176 return status().IsUnauthenticated();
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;