Skip to content

Commit

Permalink
[struct_pack] Add support for big-endian platform (#474)
Browse files Browse the repository at this point in the history
  • Loading branch information
poor-circle authored Oct 13, 2023
1 parent 44ea4e4 commit 71692db
Show file tree
Hide file tree
Showing 23 changed files with 792 additions and 318 deletions.
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

0 comments on commit 71692db

Please sign in to comment.