Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[struct_pack] Add support for big-endian platform #474

Merged
merged 32 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/s390x.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: IBM S390X

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:

jobs:
build-ubuntu-s390x:
name: Build Linux on s390x arch and run unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: uraimo/run-on-arch-action@v2
name: Test
id: runcmd
with:
arch: s390x
distro: ubuntu22.04
githubToken: ${{ github.token }}
install: |
apt-get update -q -y
apt-get -y install cmake
apt-get -y install make
apt-get -y install g++
run: |
lscpu | grep Endian
CXX=g++ CC=gcc
cmake -B ${{github.workspace}}/build \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_STRUCT_PB=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARK=OFF
cmake --build ${{github.workspace}}/build -j
cd ${{github.workspace}}/build/output/tests
./struct_pack_test
10 changes: 9 additions & 1 deletion cmake/build.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
message(STATUS "-------------COMPILE Setting-------------")
message(STATUS "-------------COMPILE SETTING-------------")

# CPP Standard
foreach(i ${CMAKE_CXX_COMPILE_FEATURES})
Expand Down Expand Up @@ -31,6 +31,14 @@ if(BUILD_WITH_LIBCXX AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
else()
endif()

include (TestBigEndian)
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
if(IS_BIG_ENDIAN)
message(STATUS "ENDIAN: BIG")
else()
message(STATUS "ENDIAN: LITTLE")
endif()

# force use lld if your compiler is clang

# When using coro_rpc_client to send request, only remote function declarations are required.
Expand Down
6 changes: 3 additions & 3 deletions cmake/subdir.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ foreach(child ${children})
get_filename_component(subdir_name ${child} NAME)
string(TOUPPER ${subdir_name} subdir_name)
if (BUILD_${subdir_name})
if(BUILD_UNIT_TESTS AND EXISTS ${child}/examples)
if(BUILD_EXAMPLES AND EXISTS ${child}/examples)
add_subdirectory(${child}/examples)
endif()
if(BUILD_BENCHMARK AND EXISTS ${child}/tests)
if(BUILD_UNIT_TESTS AND EXISTS ${child}/tests)
add_subdirectory(${child}/tests)
endif()
if(BUILD_EXAMPLES AND EXISTS ${child}/benchmark)
if(BUILD_BENCHMARK AND EXISTS ${child}/benchmark)
add_subdirectory(${child}/benchmark)
endif()
endif()
Expand Down
206 changes: 206 additions & 0 deletions include/ylt/struct_pack/endian_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* 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/util.h"

namespace struct_pack::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>
void write_wrapper(writer_t& writer, const char* 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>
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 reader_t>
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>
bool read_bytes_array(reader_t& reader, char* SP_RESTRICT data,
std::size_t length) {
if SP_UNLIKELY (length >= PTRDIFF_MAX)
unreachable();
else
return static_cast<bool>(reader.read(data, length));
}
}; // namespace struct_pack::detail
8 changes: 8 additions & 0 deletions include/ylt/struct_pack/marco.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,11 @@
#define STRUCT_PACK_RTTI_ENABLED
#endif
#endif

#if defined __clang__ || __GNUC__
#define SP_RESTRICT __restrict__
#elif defined _MSC_VER
#define SP_RESTRICT __restrict
#else
#define SP_RESTRICT
#endif
Loading
Loading