| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you 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 "core/extended_types.h" | |
| #include "core/uint24.h" | |
| #include "util/unaligned.h" | |
| namespace doris { | |
| // This header used to lean on the bswap_16/32/64 macros that protobuf's | |
| // stubs/port.h happened to leak into most TUs; use the compiler builtins | |
| // directly so it stands on its own natural include closure (redefining the | |
| // macros here would clash with protobuf's unguarded definitions). | |
| inline uint16_t gbswap_16(uint16_t host_int) { | |
| return __builtin_bswap16(host_int); | |
| } | |
| inline uint32_t gbswap_32(uint32_t host_int) { | |
| return __builtin_bswap32(host_int); | |
| } | |
| inline uint64_t gbswap_64(uint64_t host_int) { | |
| #if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) | |
| // Adapted from /usr/include/byteswap.h. Not available on Mac. | |
| if (__builtin_constant_p(host_int)) { | |
| return __bswap_constant_64(host_int); | |
| } else { | |
| uint64_t result; | |
| __asm__("bswap %0" : "=r"(result) : "0"(host_int)); | |
| return result; | |
| } | |
| #else | |
| return __builtin_bswap64(host_int); | |
| #endif | |
| } | |
| inline unsigned __int128 gbswap_128(unsigned __int128 host_int) { | |
| return static_cast<unsigned __int128>(gbswap_64(static_cast<uint64_t>(host_int >> 64))) | | |
| (static_cast<unsigned __int128>(gbswap_64(static_cast<uint64_t>(host_int))) << 64); | |
| } | |
| inline wide::UInt256 gbswap_256(wide::UInt256 host_int) { | |
| wide::UInt256 result {gbswap_64(host_int.items[3]), gbswap_64(host_int.items[2]), | |
| gbswap_64(host_int.items[1]), gbswap_64(host_int.items[0])}; | |
| return result; | |
| } | |
| // Swap bytes of a 24-bit value. | |
| inline uint32_t bswap_24(uint32_t x) { | |
| return uint32_t((x & 0x0000ffULL) << 16) | ((x & 0x00ff00ULL)) | ((x & 0xff0000ULL) >> 16); | |
| } | |
| // use std::byteswap after doris enable cpp23 | |
| template <typename T> | |
| T byte_swap(T x) { | |
| if constexpr (sizeof(T) == sizeof(wide::Int256)) { | |
| return gbswap_256(x); | |
| } else if constexpr (sizeof(T) == sizeof(__int128)) { | |
| return gbswap_128(x); | |
| } else if constexpr (sizeof(T) == sizeof(int64_t)) { | |
| return gbswap_64(x); | |
| } else if constexpr (sizeof(T) == sizeof(int32_t)) { | |
| return gbswap_32(x); | |
| } else if constexpr (sizeof(T) == sizeof(doris::uint24_t)) { | |
| return bswap_24(x); | |
| } else if constexpr (sizeof(T) == sizeof(int16_t)) { | |
| return gbswap_16(x); | |
| } else { | |
| static_assert(sizeof(T) == 1, "Unsupported type size for byte_swap"); | |
| return x; // No byte swap needed for unsupported types | |
| } | |
| } | |
| template <std::endian target, typename T> | |
| T to_endian(T value) { | |
| if constexpr (std::endian::native == target) { | |
| return value; // No swap needed | |
| } else { | |
| static_assert(std::endian::native == std::endian::big || | |
| std::endian::native == std::endian::little, | |
| "Unsupported endianness"); | |
| return byte_swap(value); | |
| } | |
| } | |
| // Utilities to convert numbers between the current hosts's native byte | |
| // order and little-endian byte order | |
| // | |
| // Load/Store methods are alignment safe | |
| class LittleEndian { | |
| public: | |
| // Functions to do unaligned loads and stores in little-endian order. | |
| static uint16_t Load16(const void* p) { | |
| return to_endian<std::endian::little>(unaligned_load<uint16_t>(p)); | |
| } | |
| static void Store16(void* p, uint16_t v) { | |
| unaligned_store<uint16_t>(p, to_endian<std::endian::little>(v)); | |
| } | |
| static uint32_t Load32(const void* p) { | |
| return to_endian<std::endian::little>(unaligned_load<uint32_t>(p)); | |
| } | |
| static void Store32(void* p, uint32_t v) { | |
| unaligned_store<uint32_t>(p, to_endian<std::endian::little>(v)); | |
| } | |
| static uint64_t Load64(const void* p) { | |
| return to_endian<std::endian::little>(unaligned_load<uint64_t>(p)); | |
| } | |
| static void Store64(void* p, uint64_t v) { | |
| unaligned_store<uint64_t>(p, to_endian<std::endian::little>(v)); | |
| } | |
| }; | |
| // Utilities to convert numbers between the current hosts's native byte | |
| // order and big-endian byte order (same as network byte order) | |
| // | |
| // Load/Store methods are alignment safe | |
| class BigEndian { | |
| public: | |
| // Functions to do unaligned loads and stores in little-endian order. | |
| static uint16_t Load16(const void* p) { | |
| return to_endian<std::endian::big>(unaligned_load<uint16_t>(p)); | |
| } | |
| static void Store16(void* p, uint16_t v) { | |
| unaligned_store<uint16_t>(p, to_endian<std::endian::big>(v)); | |
| } | |
| static uint32_t Load32(const void* p) { | |
| return to_endian<std::endian::big>(unaligned_load<uint32_t>(p)); | |
| } | |
| static void Store32(void* p, uint32_t v) { | |
| unaligned_store<uint32_t>(p, to_endian<std::endian::big>(v)); | |
| } | |
| static uint64_t Load64(const void* p) { | |
| return to_endian<std::endian::big>(unaligned_load<uint64_t>(p)); | |
| } | |
| static void Store64(void* p, uint64_t v) { | |
| unaligned_store<uint64_t>(p, to_endian<std::endian::big>(v)); | |
| } | |
| }; // BigEndian | |
| } // namespace doris |