yalantinglibs/include/ylt/struct_pack/endian_wrapper.hpp

273 lines
8.9 KiB
C++

/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* 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
*
* http://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 <bit>
#include <cstdint>
#include <type_traits>
#include "reflection.hpp"
#include "ylt/struct_pack/error_code.hpp"
#include "ylt/struct_pack/marco.h"
#include "ylt/struct_pack/util.h"
namespace struct_pack {
namespace detail {
#if __cpp_lib_endian >= 201907L
constexpr inline bool is_system_little_endian =
(std::endian::little == std::endian::native);
static_assert(std::endian::native == std::endian::little ||
std::endian::native == std::endian::big,
"struct_pack don't support middle-endian");
#else
#define BYTEORDER_LITTLE_ENDIAN 0 // Little endian machine.
#define BYTEORDER_BIG_ENDIAN 1 // Big endian machine.
//#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#ifndef BYTEORDER_ENDIAN
// Detect with GCC 4.6's macro.
#if defined(__BYTE_ORDER__)
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#else
#error \
"Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#endif
// Detect with GLIBC's endian.h.
#elif defined(__GLIBC__)
#include <endian.h>
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif (__BYTE_ORDER == __BIG_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#else
#error \
"Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#endif
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro.
#elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
// Detect with architecture macros.
#elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || \
defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || \
defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || \
defined(__s390__)
#define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || \
defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || \
defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || \
defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || \
defined(_M_X64) || defined(__bfin__)
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#else
#error \
"Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#endif
#endif
constexpr inline bool is_system_little_endian =
(BYTEORDER_ENDIAN == BYTEORDER_LITTLE_ENDIAN);
#endif
template <std::size_t block_size>
constexpr inline bool is_little_endian_copyable =
is_system_little_endian || block_size == 1;
template <typename T>
T swap_endian(T u) {
union {
T u;
unsigned char u8[sizeof(T)];
} source, dest;
source.u = u;
for (size_t k = 0; k < sizeof(T); k++)
dest.u8[k] = source.u8[sizeof(T) - k - 1];
return dest.u;
}
inline uint16_t bswap16(uint16_t raw) {
#ifdef _MSC_VER
return _byteswap_ushort(raw);
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap16(raw);
#else
return swap_endian(raw);
#endif
};
inline uint32_t bswap32(uint32_t raw) {
#ifdef _MSC_VER
return _byteswap_ulong(raw);
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap32(raw);
#else
return swap_endian(raw);
#endif
};
inline uint64_t bswap64(uint64_t raw) {
#ifdef _MSC_VER
return _byteswap_uint64(raw);
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap64(raw);
#else
return swap_endian(raw);
#endif
};
template <std::size_t block_size, typename writer_t>
STRUCT_PACK_INLINE void write_wrapper(writer_t& writer,
const char* SP_RESTRICT data) {
if constexpr (is_system_little_endian || block_size == 1) {
writer.write(data, block_size);
}
else if constexpr (block_size == 2) {
auto tmp = bswap16(*(uint16_t*)data);
writer.write((char*)&tmp, block_size);
}
else if constexpr (block_size == 4) {
auto tmp = bswap32(*(uint32_t*)data);
writer.write((char*)&tmp, block_size);
}
else if constexpr (block_size == 8) {
auto tmp = bswap64(*(uint64_t*)data);
writer.write((char*)&tmp, block_size);
}
else if constexpr (block_size == 16) {
auto tmp1 = bswap64(*(uint64_t*)data),
tmp2 = bswap64(*(uint64_t*)(data + 8));
writer.write((char*)&tmp2, block_size);
writer.write((char*)&tmp1, block_size);
}
else {
static_assert(!sizeof(writer), "illegal block size(should be 1,2,4,8,16)");
}
}
template <typename writer_t>
STRUCT_PACK_INLINE void write_bytes_array(writer_t& writer, const char* data,
std::size_t length) {
if SP_UNLIKELY (length >= PTRDIFF_MAX)
unreachable();
else
writer.write(data, length);
}
template <std::size_t block_size, typename writer_t, typename T>
STRUCT_PACK_INLINE void low_bytes_write_wrapper(writer_t& writer,
const T& elem) {
static_assert(sizeof(T) >= block_size);
if constexpr (is_system_little_endian) {
const char* data = (const char*)&elem;
writer.write(data, block_size);
}
else if constexpr (block_size == sizeof(T)) {
write_wrapper<block_size>(writer, (const char*)&elem);
}
else {
const char* data = sizeof(T) - block_size + (const char*)&elem;
if constexpr (block_size == 1) {
writer.write(data, block_size);
}
else if constexpr (block_size == 2) {
auto tmp = bswap16(*(uint16_t*)data);
writer.write((char*)&tmp, block_size);
}
else if constexpr (block_size == 4) {
auto tmp = bswap32(*(uint32_t*)data);
writer.write((char*)&tmp, block_size);
}
else {
static_assert(!sizeof(writer), "illegal block size(should be 1,2,4)");
}
}
}
template <std::size_t block_size, typename reader_t>
STRUCT_PACK_INLINE bool read_wrapper(reader_t& reader, char* SP_RESTRICT data) {
if constexpr (is_system_little_endian || block_size == 1) {
return static_cast<bool>(reader.read(data, block_size));
}
else {
std::array<char, block_size> tmp;
bool res = static_cast<bool>(reader.read((char*)&tmp, block_size));
if SP_UNLIKELY (!res) {
return res;
}
if constexpr (block_size == 2) {
*(uint16_t*)data = bswap16(*(uint16_t*)&tmp);
}
else if constexpr (block_size == 4) {
*(uint32_t*)data = bswap32(*(uint32_t*)&tmp);
}
else if constexpr (block_size == 8) {
*(uint64_t*)data = bswap64(*(uint64_t*)&tmp);
}
else if constexpr (block_size == 16) {
*(uint64_t*)(data + 8) = bswap64(*(uint64_t*)&tmp);
*(uint64_t*)data = bswap64(*(uint64_t*)(&tmp + 8));
}
else {
static_assert(!sizeof(reader),
"illegal block size(should be 1,2,4,8,16)");
}
return true;
}
}
template <typename reader_t>
STRUCT_PACK_INLINE bool read_bytes_array(reader_t& reader,
char* SP_RESTRICT data,
std::size_t length) {
return static_cast<bool>(reader.read(data, length));
}
template <std::size_t block_size, typename reader_t, typename T>
STRUCT_PACK_INLINE bool low_bytes_read_wrapper(reader_t& reader, T& elem) {
static_assert(sizeof(T) >= block_size);
if constexpr (is_system_little_endian) {
char* data = (char*)&elem;
return static_cast<bool>(reader.read(data, block_size));
}
else if constexpr (block_size == sizeof(T)) {
return read_wrapper<block_size>(reader, (char*)&elem);
}
else {
char* data = (char*)&elem + sizeof(T) - block_size;
if constexpr (block_size > 1) {
char tmp[block_size];
bool res = static_cast<bool>(reader.read(tmp, block_size));
if SP_UNLIKELY (!res) {
return res;
}
if constexpr (block_size == 2) {
*(uint16_t*)data = bswap16(*(uint16_t*)tmp);
}
else if constexpr (block_size == 4) {
*(uint32_t*)data = bswap32(*(uint32_t*)tmp);
}
else {
static_assert(!sizeof(reader), "illegal block size(should be 1,2,4)");
}
return true;
}
else {
return static_cast<bool>(reader.read(data, block_size));
}
}
}
} // namespace detail
}; // namespace struct_pack