| // Copyright 2020 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include <cstddef> |
| #include <type_traits> |
| |
| #include "pw_status/status.h" |
| |
| namespace pw { |
| |
| class StatusWithSize; |
| |
| namespace internal { |
| |
| // TODO(pwbug/268): Remove this class after migrating to the helper functions. |
| template <int kStatusShift> |
| class StatusWithSizeConstant { |
| private: |
| friend class ::pw::StatusWithSize; |
| |
| explicit constexpr StatusWithSizeConstant(Status::Code value) |
| : value_(static_cast<size_t>(value) << kStatusShift) {} |
| |
| const size_t value_; |
| }; |
| |
| } // namespace internal |
| |
| // StatusWithSize stores a status and an unsigned integer. The integer must not |
| // exceed StatusWithSize::max_size(), which is 134,217,727 (2**27 - 1) on 32-bit |
| // systems. |
| // |
| // StatusWithSize is useful for reporting the number of bytes read or written in |
| // an operation along with the status. For example, a function that writes a |
| // formatted string may want to report both the number of characters written and |
| // whether it ran out of space. |
| // |
| // StatusWithSize is more efficient than its alternatives. It packs a status and |
| // size into a single word, which can be returned from a function in a register. |
| // Because they are packed together, the size is limited to max_size(). |
| // |
| // StatusWithSize's alternatives result in larger code size. For example: |
| // |
| // 1. Return status, pass size output as a pointer argument. |
| // |
| // Requires an additional argument and forces the output argument to the |
| // stack in order to pass an address, increasing code size. |
| // |
| // 2. Return an object with Status and size members. |
| // |
| // At least for ARMv7-M, the returned struct is created on the stack, which |
| // increases code size. |
| // |
| class StatusWithSize { |
| private: |
| static constexpr size_t kStatusBits = 5; |
| static constexpr size_t kSizeMask = ~static_cast<size_t>(0) >> kStatusBits; |
| static constexpr size_t kStatusMask = ~kSizeMask; |
| static constexpr size_t kStatusShift = sizeof(size_t) * 8 - kStatusBits; |
| |
| using Constant = internal::StatusWithSizeConstant<kStatusShift>; |
| |
| public: |
| // Non-OK StatusWithSizes can be constructed from these constants, such as: |
| // |
| // StatusWithSize result = StatusWithSize::NOT_FOUND; |
| // |
| // These constants are DEPRECATED! Use the helper functions below instead. For |
| // example, change StatusWithSize::NOT_FOUND to StatusWithSize::NotFound(). |
| // |
| // TODO(pwbug/268): Migrate to the functions and remove these constants. |
| static constexpr Constant CANCELLED{Status::CANCELLED}; |
| static constexpr Constant UNKNOWN{Status::UNKNOWN}; |
| static constexpr Constant INVALID_ARGUMENT{Status::INVALID_ARGUMENT}; |
| static constexpr Constant DEADLINE_EXCEEDED{Status::DEADLINE_EXCEEDED}; |
| static constexpr Constant NOT_FOUND{Status::NOT_FOUND}; |
| static constexpr Constant ALREADY_EXISTS{Status::ALREADY_EXISTS}; |
| static constexpr Constant PERMISSION_DENIED{Status::PERMISSION_DENIED}; |
| static constexpr Constant RESOURCE_EXHAUSTED{Status::RESOURCE_EXHAUSTED}; |
| static constexpr Constant FAILED_PRECONDITION{Status::FAILED_PRECONDITION}; |
| static constexpr Constant ABORTED{Status::ABORTED}; |
| static constexpr Constant OUT_OF_RANGE{Status::OUT_OF_RANGE}; |
| static constexpr Constant UNIMPLEMENTED{Status::UNIMPLEMENTED}; |
| static constexpr Constant INTERNAL{Status::INTERNAL}; |
| static constexpr Constant UNAVAILABLE{Status::UNAVAILABLE}; |
| static constexpr Constant DATA_LOSS{Status::DATA_LOSS}; |
| static constexpr Constant UNAUTHENTICATED{Status::UNAUTHENTICATED}; |
| |
| // Functions that create a StatusWithSize with the specified status code. For |
| // codes other than OK, the size defaults to 0. |
| static constexpr StatusWithSize Ok(size_t size) { |
| return StatusWithSize(size); |
| } |
| static constexpr StatusWithSize Cancelled(size_t size = 0) { |
| return StatusWithSize(Status::Cancelled(), size); |
| } |
| static constexpr StatusWithSize Unknown(size_t size = 0) { |
| return StatusWithSize(Status::Unknown(), size); |
| } |
| static constexpr StatusWithSize InvalidArgument(size_t size = 0) { |
| return StatusWithSize(Status::InvalidArgument(), size); |
| } |
| static constexpr StatusWithSize DeadlineExceeded(size_t size = 0) { |
| return StatusWithSize(Status::DeadlineExceeded(), size); |
| } |
| static constexpr StatusWithSize NotFound(size_t size = 0) { |
| return StatusWithSize(Status::NotFound(), size); |
| } |
| static constexpr StatusWithSize AlreadyExists(size_t size = 0) { |
| return StatusWithSize(Status::AlreadyExists(), size); |
| } |
| static constexpr StatusWithSize PermissionDenied(size_t size = 0) { |
| return StatusWithSize(Status::PermissionDenied(), size); |
| } |
| static constexpr StatusWithSize Unauthenticated(size_t size = 0) { |
| return StatusWithSize(Status::Unauthenticated(), size); |
| } |
| static constexpr StatusWithSize ResourceExhausted(size_t size = 0) { |
| return StatusWithSize(Status::ResourceExhausted(), size); |
| } |
| static constexpr StatusWithSize FailedPrecondition(size_t size = 0) { |
| return StatusWithSize(Status::FailedPrecondition(), size); |
| } |
| static constexpr StatusWithSize Aborted(size_t size = 0) { |
| return StatusWithSize(Status::Aborted(), size); |
| } |
| static constexpr StatusWithSize OutOfRange(size_t size = 0) { |
| return StatusWithSize(Status::OutOfRange(), size); |
| } |
| static constexpr StatusWithSize Unimplemented(size_t size = 0) { |
| return StatusWithSize(Status::Unimplemented(), size); |
| } |
| static constexpr StatusWithSize Internal(size_t size = 0) { |
| return StatusWithSize(Status::Internal(), size); |
| } |
| static constexpr StatusWithSize Unavailable(size_t size = 0) { |
| return StatusWithSize(Status::Unavailable(), size); |
| } |
| static constexpr StatusWithSize DataLoss(size_t size = 0) { |
| return StatusWithSize(Status::DataLoss(), size); |
| } |
| |
| // Creates a StatusWithSize with Status::Ok() and a size of 0. |
| explicit constexpr StatusWithSize() : size_(0) {} |
| |
| // Creates a StatusWithSize with Status::Ok() and the provided size. |
| // std::enable_if is used to prevent enum types (e.g. Status) from being used. |
| // TODO(hepler): Add debug-only assert that size <= max_size(). |
| template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>> |
| explicit constexpr StatusWithSize(T size) : size_(size) {} |
| |
| // Creates a StatusWithSize with the provided status and size. |
| explicit constexpr StatusWithSize(Status status, size_t size) |
| : StatusWithSize((static_cast<size_t>(status) << kStatusShift) | size) {} |
| |
| // Allow implicit conversions from the StatusWithSize constants. |
| constexpr StatusWithSize(Constant constant) : size_(constant.value_) {} |
| |
| constexpr StatusWithSize(const StatusWithSize&) = default; |
| constexpr StatusWithSize& operator=(const StatusWithSize&) = default; |
| |
| // Returns the size. The size is always present, even if status() is an error. |
| constexpr size_t size() const { return size_ & kSizeMask; } |
| |
| // The maximum valid value for size. |
| static constexpr size_t max_size() { return kSizeMask; } |
| |
| // True if status() == Status::OK. |
| constexpr bool ok() const { return (size_ & kStatusMask) == 0u; } |
| |
| constexpr Status status() const { |
| return static_cast<Status::Code>((size_ & kStatusMask) >> kStatusShift); |
| } |
| |
| private: |
| size_t size_; |
| }; |
| |
| } // namespace pw |