blob: 0ff854811e24c96fed3d3a80476f9bbe47ac5132 [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 <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