diff --git a/CMakeLists.txt b/CMakeLists.txt index 00837d4..3988497 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,6 +297,7 @@ if(TILE_BUILD_TESTS) tile_add_test(io_util_rate_limiter_test "tile/io/util/rate_limiter_test.cc") tile_add_test(base_exposed_var_test "tile/base/exposed_var_test.cc") + tile_add_test(base_byte_order_test "tile/base/byte_order_test.cc") # tile_add_test(fiber_detail_scheduler_test "tile/fiber/detail/scheduler_test.cc") tile_add_test(base_internal_meta_test "tile/base/internal/meta_test.cc") # tile_add_test(net_internal_http_engine_test diff --git a/tile/base/byte_order.h b/tile/base/byte_order.h new file mode 100644 index 0000000..7ac6fae --- /dev/null +++ b/tile/base/byte_order.h @@ -0,0 +1,126 @@ +#ifndef TILE_BASE_BYTE_ORDER_H +#define TILE_BASE_BYTE_ORDER_H + +#pragma once + +#include +#define TILE_BYTE_ORDER __BYTE_ORDER__ +#define TILE_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#define TILE_BIG_ENDIAN __ORDER_BIG_ENDIAN__ + +#ifdef __linux__ +#include +#else + +#if TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN +#define htobe16(v) htons(v) +#define htobe32(v) htonl(v) +#define be16toh(v) ntohs(v) +#define be32toh(v) ntohl(v) +#define htole16(v) (v) +#define htole32(v) (v) +#define htole64(v) (v) +#define le16toh(v) (v) +#define le32toh(v) (v) +#define le64toh(v) (v) +#elif TILE_BYTE_ORDER == TILE_BIG_ENDIAN +#define htobe16(v) (v) +#define htobe32(v) (v) +#define be16toh(v) (v) +#define be32toh(v) (v) +#define htole16(v) __builtin_bswap16(v) +#define htole32(v) __builtin_bswap32(v) +#define htole64(v) __builtin_bswap64(v) +#define le16toh(v) __builtin_bswap16(v) +#define le32toh(v) __builtin_bswap32(v) +#define le64toh(v) __builtin_bswap64(v) +#else +#error "Unsupported byte order" +#endif +#endif + +namespace tile { + +inline uint16_t ByteSwap16(uint16_t val) { return __builtin_bswap16(val); } +inline uint32_t ByteSwap32(uint32_t val) { return __builtin_bswap32(val); } +inline uint64_t ByteSwap64(uint64_t val) { return __builtin_bswap64(val); } + +inline bool IsHostLittleEndian() { +#if TILE_LITTLE_ENDIAN == TILE_BYTE_ORDER + return true; +#else + return false; +#endif +} + +inline bool IsHostBigEndian() { +#if TILE_BIG_ENDIAN == TILE_BYTE_ORDER + return true; +#else + return false; +#endif +} + +inline uint16_t HostToNetwork16(uint16_t val_in_host) { +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + return val_in_host; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + return ByteSwap16(val_in_host); +#else +#error "Unsupported byte order" +#endif +} + +inline uint32_t HostToNetwork32(uint32_t val_in_host) { +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + return val_in_host; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + return ByteSwap32(val_in_host); +#else +#error "Unsupported byte order" +#endif +} + +inline uint64_t HostToNetwork64(uint64_t val_in_host) { +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + return val_in_host; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + return ByteSwap64(val_in_host); +#else +#error "Unsupported byte order" +#endif +} + +inline uint16_t NetworkToHost16(uint16_t val_in_network) { +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + return val_in_network; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + return ByteSwap16(val_in_network); +#else +#error "Unsupported byte order" +#endif +} + +inline uint32_t NetworkToHost32(uint32_t val_in_network) { +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + return val_in_network; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + return ByteSwap32(val_in_network); +#else +#error "Unsupported byte order" +#endif +} + +inline uint64_t NetworkToHost64(uint64_t val_in_network) { +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + return val_in_network; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + return ByteSwap64(val_in_network); +#else +#error "Unsupported byte order" +#endif +} + +} // namespace tile + +#endif // TILE_BASE_BYTE_ORDER_H diff --git a/tile/base/byte_order_test.cc b/tile/base/byte_order_test.cc new file mode 100644 index 0000000..8bb17bd --- /dev/null +++ b/tile/base/byte_order_test.cc @@ -0,0 +1,116 @@ +#include "tile/base/byte_order.h" + +#include "gtest/gtest.h" +#ifdef __linux__ +#include +#endif + +namespace tile { + +static union EndianHelper { + uint16_t v16; + uint32_t v32; + uint32_t v64; + uint8_t bytes[8]; +} endian_helper = {.bytes = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}; + +TEST(ByteOrder, ByteSwap16) { + const uint16_t val = 0x1234; + const uint16_t expected = 0x3412; + ASSERT_EQ(expected, ByteSwap16(val)); +} +TEST(ByteOrder, ByteSwap32) { + const uint32_t val = 0x12345678; + const uint32_t expected = 0x78563412; + ASSERT_EQ(expected, ByteSwap32(val)); +} +TEST(ByteOrder, ByteSwap64) { + const uint64_t val = 0x1234567890abcdef; + const uint64_t expected = 0xefcdab9078563412; + ASSERT_EQ(expected, ByteSwap64(val)); +} + +TEST(ByteOrder, IsHostByteOrder) { + ASSERT_NE(IsHostBigEndian(), IsHostLittleEndian()); +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + ASSERT_TRUE(IsHostBigEndian()); + ASSERT_FALSE(IsHostLittleEndian()); +#else + ASSERT_TRUE(IsHostLittleEndian()); + ASSERT_FALSE(IsHostBigEndian()); +#endif +} + +TEST(ByteOrder, HostToNetwork16) { + const uint16_t val = 0x1234; +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + const uint16_t expected = val; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + const uint16_t expected = 0x3412; +#else +#error "Unsupported byte order" +#endif + ASSERT_EQ(expected, HostToNetwork16(val)); +} + +TEST(ByteOrder, HostToNetwork32) { + const uint32_t val = 0x12345678; +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + const uint32_t expected = val; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + const uint32_t expected = 0x78563412; +#else +#error "Unsupported byte order" +#endif + ASSERT_EQ(expected, HostToNetwork32(val)); +} + +TEST(ByteOrder, HostToNetwork64) { + const uint64_t val = 0x1234567890abcdef; +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + const uint64_t expected = val; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + const uint64_t expected = 0xefcdab9078563412; +#else +#error "Unsupported byte order" +#endif + ASSERT_EQ(expected, HostToNetwork64(val)); +} + +TEST(ByteOrder, NetworkToHost16) { + const uint16_t val = 0x1234; +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + const uint16_t expected = val; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + const uint16_t expected = 0x3412; +#else +#error "Unsupported byte order" +#endif + ASSERT_EQ(expected, NetworkToHost16(val)); +} + +TEST(ByteOrder, NetworkToHost32) { + const uint32_t val = 0x12345678; +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + const uint32_t expected = val; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + const uint32_t expected = 0x78563412; +#else +#error "Unsupported byte order" +#endif + ASSERT_EQ(expected, NetworkToHost32(val)); +} + +TEST(ByteOrder, NetworkToHost64) { + const uint64_t val = 0x1234567890abcdef; +#if TILE_BYTE_ORDER == TILE_BIG_ENDIAN + const uint64_t expected = val; +#elif TILE_BYTE_ORDER == TILE_LITTLE_ENDIAN + const uint64_t expected = 0xefcdab9078563412; +#else +#error "Unsupported byte order" +#endif + ASSERT_EQ(expected, NetworkToHost64(val)); +} + +} // namespace tile diff --git a/tile/base/net/endpoint.cc b/tile/base/net/endpoint.cc index a37cada..f7d6ee3 100644 --- a/tile/base/net/endpoint.cc +++ b/tile/base/net/endpoint.cc @@ -1,5 +1,6 @@ #include "tile/base/net/endpoint.h" +#include "tile/base/byte_order.h" #include "tile/base/config.h" #include "tile/base/string.h" @@ -27,16 +28,16 @@ std::string SockAddrToString(const sockaddr *addr, socklen_t length) { switch (af) { case AF_INET: { auto p = reinterpret_cast(addr); - uint32_t s_addr = ntohl(p->sin_addr.s_addr); + uint32_t s_addr = NetworkToHost32(p->sin_addr.s_addr); return Format("{}.{}.{}.{}:{}", (s_addr >> 24) & 0xff, (s_addr >> 16) & 0xff, (s_addr >> 8) & 0xff, s_addr & 0xff, - ntohs(p->sin_port)); + NetworkToHost16(p->sin_port)); } case AF_INET6: { auto p = reinterpret_cast(addr); char ip[INET6_ADDRSTRLEN]; TILE_CHECK(inet_ntop(af, &p->sin6_addr, ip, INET6_ADDRSTRLEN)); - return Format("[{}]:{}", ip, ntohs(p->sin6_port)); + return Format("[{}]:{}", ip, NetworkToHost16(p->sin6_port)); } case AF_UNIX: { auto p = reinterpret_cast(addr); @@ -119,7 +120,7 @@ Endpoint EndpointFromIpv4(const std::string &ip, std::uint16_t port) { memset(p, 0, sizeof(sockaddr_in)); TILE_PCHECK(inet_pton(AF_INET, ip.c_str(), &p->sin_addr), "Cannot parse [{}]", ip); - p->sin_port = htons(port); + p->sin_port = HostToNetwork16(port); p->sin_family = AF_INET; *er.RetrieveLength() = sizeof(sockaddr_in); return er.Build(); @@ -132,7 +133,7 @@ Endpoint EndpointFromIpv6(const std::string &ip, std::uint16_t port) { memset(p, 0, sizeof(sockaddr_in6)); TILE_PCHECK(inet_pton(AF_INET6, ip.c_str(), &p->sin6_addr), "Cannot parse [{}]", ip); - p->sin6_port = htons(port); + p->sin6_port = HostToNetwork16(port); p->sin6_family = AF_INET6; *er.RetrieveLength() = sizeof(sockaddr_in6); return er.Build(); @@ -186,10 +187,10 @@ std::uint16_t EndpointGetPort(const Endpoint &endpoint) { "Unexpected: Address family #{} is not valid IP address family.", endpoint.Family()); if (endpoint.Family() == AF_INET) { - return ntohs( + return NetworkToHost16( reinterpret_cast(endpoint.Get())->sin_port); } else if (endpoint.Family() == AF_INET6) { - return ntohs( + return NetworkToHost16( reinterpret_cast(endpoint.Get())->sin6_port); } TILE_NOT_IMPLEMENTED("Unsupported family [{}]", endpoint.Family()); @@ -284,7 +285,7 @@ bool IsPrivateIpv4AddressRfc(const Endpoint &addr) { return false; } - auto ip = ntohl(addr.UnsafeGet()->sin_addr.s_addr); + auto ip = NetworkToHost32(addr.UnsafeGet()->sin_addr.s_addr); for (auto &&item : kRanges) { if ((ip & item.first) == item.second) { return true; @@ -309,7 +310,7 @@ bool IsPrivateIpv4AddressCorp(const Endpoint &addr) { return false; } - auto ip = ntohl(addr.UnsafeGet()->sin_addr.s_addr); + auto ip = NetworkToHost32(addr.UnsafeGet()->sin_addr.s_addr); for (auto &&item : kRanges) { auto &mask = item.first; auto &expected = item.second; @@ -352,7 +353,7 @@ std::optional TryParseTraits::TryParse(Slice s, if (inet_pton(AF_INET, ip.c_str(), &p->sin_addr) != 1) { return {}; } - p->sin_port = htons(*port); + p->sin_port = HostToNetwork16(*port); p->sin_family = AF_INET; *er.RetrieveLength() = sizeof(sockaddr_in); return er.Build(); @@ -379,7 +380,7 @@ std::optional TryParseTraits::TryParse(Slice s, if (inet_pton(AF_INET6, ip.c_str(), &p->sin6_addr) != 1) { return {}; } - p->sin6_port = htons(*port); + p->sin6_port = HostToNetwork16(*port); p->sin6_family = AF_INET6; *er.RetrieveLength() = sizeof(sockaddr_in6); return er.Build();