| // 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 <array> |
| #include <bit> |
| #include <cstring> |
| #include <type_traits> |
| |
| #include "pw_bytes/array.h" |
| |
| namespace pw::bytes { |
| namespace internal { |
| |
| // Use a struct rather than an alias to give the type a more reasonable name. |
| template <typename T> |
| struct EquivalentUintImpl |
| : std::conditional< |
| sizeof(T) == 1, |
| uint8_t, |
| std::conditional_t< |
| sizeof(T) == 2, |
| uint16_t, |
| std::conditional_t< |
| sizeof(T) == 4, |
| uint32_t, |
| std::conditional_t<sizeof(T) == 8, uint64_t, void>>>> { |
| static_assert(std::is_integral_v<T>); |
| }; |
| |
| template <typename T> |
| using EquivalentUint = typename EquivalentUintImpl<T>::type; |
| |
| template <typename T> |
| constexpr std::array<std::byte, sizeof(T)> CopyLittleEndian(T value) { |
| return CopyLittleEndian(static_cast<EquivalentUint<T>>(value)); |
| } |
| |
| template <> |
| constexpr std::array<std::byte, 1> CopyLittleEndian<uint8_t>(uint8_t value) { |
| return MakeArray(value); |
| } |
| template <> |
| constexpr std::array<std::byte, 2> CopyLittleEndian<uint16_t>(uint16_t value) { |
| return MakeArray(value & 0x00FF, (value & 0xFF00) >> 8); |
| } |
| |
| template <> |
| constexpr std::array<std::byte, 4> CopyLittleEndian<uint32_t>(uint32_t value) { |
| return MakeArray((value & 0x000000FF) >> 0 * 8, |
| (value & 0x0000FF00) >> 1 * 8, |
| (value & 0x00FF0000) >> 2 * 8, |
| (value & 0xFF000000) >> 3 * 8); |
| } |
| |
| template <> |
| constexpr std::array<std::byte, 8> CopyLittleEndian<uint64_t>(uint64_t value) { |
| return MakeArray((value & 0x00000000000000FF) >> 0 * 8, |
| (value & 0x000000000000FF00) >> 1 * 8, |
| (value & 0x0000000000FF0000) >> 2 * 8, |
| (value & 0x00000000FF000000) >> 3 * 8, |
| (value & 0x000000FF00000000) >> 4 * 8, |
| (value & 0x0000FF0000000000) >> 5 * 8, |
| (value & 0x00FF000000000000) >> 6 * 8, |
| (value & 0xFF00000000000000) >> 7 * 8); |
| } |
| |
| template <typename T> |
| constexpr T ReverseBytes(T value) { |
| EquivalentUint<T> uint = static_cast<EquivalentUint<T>>(value); |
| |
| if constexpr (sizeof(uint) == 1) { |
| return static_cast<T>(uint); |
| } else if constexpr (sizeof(uint) == 2) { |
| return static_cast<T>(((uint & 0x00FF) << 8) | ((uint & 0xFF00) >> 8)); |
| } else if constexpr (sizeof(uint) == 4) { |
| return static_cast<T>(((uint & 0x000000FF) << 3 * 8) | // |
| ((uint & 0x0000FF00) << 1 * 8) | // |
| ((uint & 0x00FF0000) >> 1 * 8) | // |
| ((uint & 0xFF000000) >> 3 * 8)); |
| } else { |
| static_assert(sizeof(uint) == 8); |
| return static_cast<T>(((uint & 0x00000000000000FF) << 7 * 8) | // |
| ((uint & 0x000000000000FF00) << 5 * 8) | // |
| ((uint & 0x0000000000FF0000) << 3 * 8) | // |
| ((uint & 0x00000000FF000000) << 1 * 8) | // |
| ((uint & 0x000000FF00000000) >> 1 * 8) | // |
| ((uint & 0x0000FF0000000000) >> 3 * 8) | // |
| ((uint & 0x00FF000000000000) >> 5 * 8) | // |
| ((uint & 0xFF00000000000000) >> 7 * 8)); |
| } |
| } |
| |
| } // namespace internal |
| |
| // Functions for reordering bytes in the provided integral value to match the |
| // specified byte order. These functions are similar to the htonl() family of |
| // functions. |
| // |
| // If the value is converted to non-system endianness, it must NOT be used |
| // directly, since the value will be meaningless. Such values are only suitable |
| // to memcpy'd or sent to a different device. |
| template <typename T> |
| constexpr T ConvertOrder(std::endian from, std::endian to, T value) { |
| return from == to ? value : internal::ReverseBytes(value); |
| } |
| |
| // Converts a value from native byte order to the specified byte order. Since |
| // this function changes the value's endianness, the result should only be used |
| // to memcpy the bytes to a buffer or send to a different device. |
| template <typename T> |
| constexpr T ConvertOrderTo(std::endian to_endianness, T value) { |
| return ConvertOrder(std::endian::native, to_endianness, value); |
| } |
| |
| // Converts a value from the specified byte order to the native byte order. |
| template <typename T> |
| constexpr T ConvertOrderFrom(std::endian from_endianness, T value) { |
| return ConvertOrder(from_endianness, std::endian::native, value); |
| } |
| |
| // Copies the value to a std::array with the specified endianness. |
| template <typename T> |
| constexpr auto CopyInOrder(std::endian order, T value) { |
| return internal::CopyLittleEndian(ConvertOrderTo(order, value)); |
| } |
| |
| } // namespace pw::bytes |