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

sys/libc: add endian.h #20310

Merged
merged 2 commits into from
Jan 30, 2024
Merged
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
114 changes: 114 additions & 0 deletions sys/libc/include/endian.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2024 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/

/**
* @addtogroup posix
* @{
*/
/**
* @file
* @brief libc header for endian conversion
*
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
*/

#ifndef ENDIAN_H
#define ENDIAN_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifdef DOXYGEN
/**
* @brief A numeric constant representing little endian byte order
*/
#define LITTLE_ENDIAN implementation_defined

/**
* @brief A numeric constant representing big endian byte order
*/
#define BIG_ENDIAN implementation_defined

/**
* @brief A numeric constant representing PDP endian byte order
*/
#define PDP_ENDIAN implementation_defined

/**
* @brief The byte order of this machines indicated by the constant
* @ref BIG_ENDIAN or @ref LITTLE_ENDIAN
*
* @note This numeric constant is available at preprocessor time, so you
* can compare this to @ref BIG_ENDIAN or @ref LITTLE_ENDIAN in
* `#if` directives.
*/
#define BYTE_ORDER <LITTLE_ENDIAN or BIG_ENDIAN>

uint16_t htobe16(uint16_t host_16bits); /**< host to big endian, 16 bit */
uint16_t htole16(uint16_t host_16bits); /**< host to little endian, 16 bit */
uint16_t be16toh(uint16_t big_endian_16bits); /**< big endian to host, 16 bit */
uint16_t le16toh(uint16_t little_endian_16bits);/**< little endian to host, 16 bit */

uint32_t htobe32(uint32_t host_32bits); /**< host to big endian, 32 bit */
uint32_t htole32(uint32_t host_32bits); /**< host to little endian, 32 bit */
uint32_t be32toh(uint32_t big_endian_32bits); /**< big endian to host, 32 bit */
uint32_t le32toh(uint32_t little_endian_32bits);/**< little endian to host, 32 bit */

uint64_t htobe64(uint64_t host_64bits); /**< host to big endian, 64 bit */
uint64_t htole64(uint64_t host_64bits); /**< host to little endian, 64 bit */
uint64_t be64toh(uint64_t big_endian_64bits); /**< big endian to host, 64 bit */
uint64_t le64toh(uint64_t little_endian_64bits);/**< little endian to host, 64 bit */

#else /* DOXYGEN */

#define LITTLE_ENDIAN _LITTLE_ENDIAN
#define BIG_ENDIAN _BIG_ENDIAN
#define PDP_ENDIAN _PDP_ENDIAN
#define BYTE_ORDER _BYTE_ORDER

#if BYTE_ORDER == LITTLE_ENDIAN
# define htobe16(_x) __builtin_bswap16(_x)
# define htole16(_x) ((__uint16_t)(_x))
# define be16toh(_x) __builtin_bswap16(_x)
# define le16toh(_x) ((__uint16_t)(_x))
# define htobe32(_x) __builtin_bswap32(_x)
# define htole32(_x) ((__uint32_t)(_x))
# define be32toh(_x) __builtin_bswap32(_x)
# define le32toh(_x) ((__uint32_t)(_x))
# define htobe64(_x) __builtin_bswap64(_x)
# define htole64(_x) ((__uint64_t)(_x))
# define be64toh(_x) __builtin_bswap64(_x)
# define le64toh(_x) ((__uint64_t)(_x))
#elif BYTE_ORDER == BIG_ENDIAN
# define htole16(_x) __builtin_bswap16(_x)
# define htobe16(_x) ((__uint16_t)(_x))
# define le16toh(_x) __builtin_bswap16(_x)
# define be16toh(_x) ((__uint16_t)(_x))
# define htole32(_x) __builtin_bswap32(_x)
# define htobe32(_x) ((__uint32_t)(_x))
# define le32toh(_x) __builtin_bswap32(_x)
# define be32toh(_x) ((__uint32_t)(_x))
# define htole64(_x) __builtin_bswap64(_x)
# define htobe64(_x) ((__uint64_t)(_x))
# define le64toh(_x) __builtin_bswap64(_x)
# define be64toh(_x) ((__uint64_t)(_x))
#else
# error "Byte order not supported"
#endif

#endif /* DOXYGEN */

#ifdef __cplusplus
}
#endif

#endif /* ENDIAN_H */
/** @} */
54 changes: 54 additions & 0 deletions tests/unittests/tests-libc/tests-libc.c
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@

#include <stdint.h>
#include <string.h>
#include <endian.h>
#include "string_utils.h"

#include "tests-libc.h"
@@ -39,11 +40,64 @@ static void test_libc_memchk(void)
TEST_ASSERT(memchk(buffer, 0xff, sizeof(buffer)) == &buffer[5]);
}

/**
* @name Unit test ensuring `<endian.h>` is provided and correct across
* all platforms
* @{
*/
union u16 {
uint8_t as_bytes[2];
uint16_t as_number;
};

union u32 {
uint8_t as_bytes[4];
uint32_t as_number;
};

union u64 {
uint8_t as_bytes[8];
uint64_t as_number;
};

static void test_libc_endian(void)
{
/* format the numbers 0x0102, 0x01020304 and 0x0102030405060708
* in little/big endian format by hand: */
const uint16_t u16_host = 0x0102;
const union u16 u16_be = { .as_bytes = { 0x01, 0x02 } };
const union u16 u16_le = { .as_bytes = { 0x02, 0x01 } };
const uint32_t u32_host = 0x01020304;
const union u32 u32_be = { .as_bytes = { 0x01, 0x02, 0x03, 0x04 } };
const union u32 u32_le = { .as_bytes = { 0x04, 0x03, 0x02, 0x01 } };
const uint64_t u64_host = 0x0102030405060708;
const union u64 u64_be = { .as_bytes = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } };
const union u64 u64_le = { .as_bytes = { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 } };

/* check from host to big/little endian */
TEST_ASSERT_EQUAL_INT(htobe16(u16_host), u16_be.as_number);
TEST_ASSERT_EQUAL_INT(htole16(u16_host), u16_le.as_number);
TEST_ASSERT_EQUAL_INT(htobe32(u32_host), u32_be.as_number);
TEST_ASSERT_EQUAL_INT(htole32(u32_host), u32_le.as_number);
TEST_ASSERT_EQUAL_INT(htobe64(u64_host), u64_be.as_number);
TEST_ASSERT_EQUAL_INT(htole64(u64_host), u64_le.as_number);

/* check little/big endian to host */
TEST_ASSERT_EQUAL_INT(be16toh(u16_be.as_number), u16_host);
TEST_ASSERT_EQUAL_INT(le16toh(u16_le.as_number), u16_host);
TEST_ASSERT_EQUAL_INT(be32toh(u32_be.as_number), u32_host);
TEST_ASSERT_EQUAL_INT(le32toh(u32_le.as_number), u32_host);
TEST_ASSERT_EQUAL_INT(be64toh(u64_be.as_number), u64_host);
TEST_ASSERT_EQUAL_INT(le64toh(u64_le.as_number), u64_host);
}
/** @} */

Test *tests_libc_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_libc_strscpy),
new_TestFixture(test_libc_memchk),
new_TestFixture(test_libc_endian),
};

EMB_UNIT_TESTCALLER(libc_tests, NULL, NULL, fixtures);
Loading
Oops, something went wrong.