blob: 8c79e8cf5c74ab153ba46e14bbe23bc91201dba3 [file] [log] [blame]
// 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::NotFound();
//
// These constants are DEPRECATED! Use the helper functions below instead. For
// example, change StatusWithSize::NotFound() 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::InvalidArgument()};
static constexpr Constant DEADLINE_EXCEEDED{Status::DeadlineExceeded()};
static constexpr Constant NOT_FOUND{Status::NotFound()};
static constexpr Constant ALREADY_EXISTS{Status::AlreadyExists()};
static constexpr Constant PERMISSION_DENIED{Status::PermissionDenied()};
static constexpr Constant RESOURCE_EXHAUSTED{Status::ResourceExhausted()};
static constexpr Constant FAILED_PRECONDITION{Status::FailedPrecondition()};
static constexpr Constant ABORTED{Status::Aborted()};
static constexpr Constant OUT_OF_RANGE{Status::OutOfRange()};
static constexpr Constant UNIMPLEMENTED{Status::Unimplemented()};
static constexpr Constant INTERNAL{Status::Internal()};
static constexpr Constant UNAVAILABLE{Status::Unavailable()};
static constexpr Constant DATA_LOSS{Status::DataLoss()};
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