blob: 4c8ae28482046ba9d8d204a993c0cf0ae0ffeade [file] [log] [blame]
// Copyright 2022 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.
// Low-level bit operations including std::endian from C++20.
#pragma once
#include <climits>
#include "lib/stdcompat/bit.h"
namespace pw {
using ::cpp20::endian;
namespace bytes {
/// Queries size of the object or type in bits.
#define SIZE_OF_IN_BIT(...) (sizeof(__VA_ARGS__) * CHAR_BIT)
/// Extends the nth bit to the left. Useful for expanding singed values into
/// larger integer types.
template <std::size_t kBitWidth, typename T>
constexpr T SignExtend(T nbit_value) {
static_assert(std::is_integral_v<T>);
static_assert(kBitWidth < SIZE_OF_IN_BIT(T));
using SignedT = std::make_signed_t<T>;
constexpr std::size_t extension_bits = SIZE_OF_IN_BIT(SignedT) - kBitWidth;
SignedT nbit_temp = static_cast<SignedT>(nbit_value);
return ((nbit_temp << extension_bits) >> extension_bits);
}
/// Extracts bits between msb and lsb from a value.
///
/// @tparam OutType The type of output number to be extracted from input
/// number.
/// @tparam kMsb The left bit (included) that extraction starts at.
/// @tparam kLsb The right bit (included) that extraction ends at.
/// @tparam InType The type of input number.
/// @param[in] value The input number.
///
/// Example (extrat bits between 10 and 5 from a uint32_t and return as a
/// uint8_t):
///
/// @code
/// constexpr uint32_t number = 0xA0A0A0A0;
/// constexpr uint8_t extracted_number = ExtractBits<uint8_t, 10, 5>(number);
/// @endcode
template <typename OutType, std::size_t kMsb, std::size_t kLsb, typename InType>
constexpr OutType ExtractBits(InType value) {
static_assert(kMsb >= kLsb);
static_assert(kMsb < SIZE_OF_IN_BIT(InType));
constexpr std::size_t kBitWidth = kMsb - kLsb + 1;
static_assert(kBitWidth <= SIZE_OF_IN_BIT(OutType));
if constexpr (kBitWidth == SIZE_OF_IN_BIT(InType)) {
return OutType(value);
} else {
constexpr OutType mask = OutType((OutType(1) << kBitWidth) - 1);
return OutType((value >> kLsb) & mask);
}
}
} // namespace bytes
} // namespace pw