/* * Copyright (c) 2020, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #if defined(AK_OS_MACOS) # include # include # define htobe16(x) OSSwapHostToBigInt16(x) # define htole16(x) OSSwapHostToLittleInt16(x) # define be16toh(x) OSSwapBigToHostInt16(x) # define le16toh(x) OSSwapLittleToHostInt16(x) # define htobe32(x) OSSwapHostToBigInt32(x) # define htole32(x) OSSwapHostToLittleInt32(x) # define be32toh(x) OSSwapBigToHostInt32(x) # define le32toh(x) OSSwapLittleToHostInt32(x) # define htobe64(x) OSSwapHostToBigInt64(x) # define htole64(x) OSSwapHostToLittleInt64(x) # define be64toh(x) OSSwapBigToHostInt64(x) # define le64toh(x) OSSwapLittleToHostInt64(x) # define __BIG_ENDIAN BIG_ENDIAN # define __LITTLE_ENDIAN LITTLE_ENDIAN # define __BYTE_ORDER BYTE_ORDER #endif namespace AK { template ALWAYS_INLINE constexpr T convert_between_host_and_little_endian(T value) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return value; #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ if constexpr (sizeof(T) == 8) return __builtin_bswap64(value); if constexpr (sizeof(T) == 4) return __builtin_bswap32(value); if constexpr (sizeof(T) == 2) return __builtin_bswap16(value); if constexpr (sizeof(T) == 1) return value; #endif } template ALWAYS_INLINE constexpr T convert_between_host_and_big_endian(T value) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ if constexpr (sizeof(T) == 8) return __builtin_bswap64(value); if constexpr (sizeof(T) == 4) return __builtin_bswap32(value); if constexpr (sizeof(T) == 2) return __builtin_bswap16(value); if constexpr (sizeof(T) == 1) return value; #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return value; #endif } template ALWAYS_INLINE T convert_between_host_and_network_endian(T value) { return convert_between_host_and_big_endian(value); } template class LittleEndian; template InputStream& operator>>(InputStream&, LittleEndian&); template OutputStream& operator<<(OutputStream&, LittleEndian); template class [[gnu::packed]] LittleEndian { public: friend InputStream& operator>>(InputStream&, LittleEndian&); friend OutputStream& operator<< (OutputStream&, LittleEndian); constexpr LittleEndian() = default; constexpr LittleEndian(T value) : m_value(convert_between_host_and_little_endian(value)) { } constexpr operator T() const { return convert_between_host_and_little_endian(m_value); } // This returns the internal representation. In this case, that is the value stored in little endian format. constexpr Bytes bytes() { return Bytes { &m_value, sizeof(m_value) }; } constexpr ReadonlyBytes bytes() const { return ReadonlyBytes { &m_value, sizeof(m_value) }; } private: T m_value { 0 }; }; template class BigEndian; template InputStream& operator>>(InputStream&, BigEndian&); template OutputStream& operator<<(OutputStream&, BigEndian); template class [[gnu::packed]] BigEndian { public: friend InputStream& operator>>(InputStream&, BigEndian&); friend OutputStream& operator<< (OutputStream&, BigEndian); constexpr BigEndian() = default; constexpr BigEndian(T value) : m_value(convert_between_host_and_big_endian(value)) { } constexpr operator T() const { return convert_between_host_and_big_endian(m_value); } // This returns the internal representation. In this case, that is the value stored in big endian format. constexpr Bytes bytes() { return Bytes { &m_value, sizeof(m_value) }; } constexpr ReadonlyBytes bytes() const { return ReadonlyBytes { &m_value, sizeof(m_value) }; } private: T m_value { 0 }; }; template using NetworkOrdered = BigEndian; template requires(HasFormatter) struct Formatter> : Formatter { }; template requires(HasFormatter) struct Formatter> : Formatter { }; } #if USING_AK_GLOBALLY using AK::BigEndian; using AK::LittleEndian; using AK::NetworkOrdered; #endif