master ohmu/fastfetch-openbsd / tests / endian.c
ohmu  ·  2026-10-15
 1#include "common/endian.h"
 2#include "common/textModifier.h"
 3
 4#include <stdint.h>
 5#include <stdio.h>
 6#include <stdlib.h>
 7#include <string.h>
 8
 9static void verify(bool expression, const char* expressionStr, int lineNo) {
10    if (expression) {
11        return;
12    }
13
14    fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, expressionStr);
15    exit(1);
16}
17
18#define VERIFY(expression) verify((expression), #expression, __LINE__)
19
20// The macros take a value, so the only way to test them is to lay out the bytes of a known number
21// in memory and read them back through the macro. The expectations below do not depend on the
22// endianness of the machine running the test:
23//
24//   * copying the little endian byte sequence of 0x11223344 (44 33 22 11) into a `uint32_t` yields
25//     0x11223344 on a little endian machine and 0x44332211 on a big endian one. `FF_READ_LE` has to
26//     turn both of them into 0x11223344, which means it is the identity on one machine and a byte
27//     swap on the other.
28//   * `FF_READ_BE` is the mirror image.
29//
30// On a little endian machine this only pins that the two macros are the identity / swap pair; the
31// selection itself (a compiler that does not define `__BIG_ENDIAN__` at all must still be detected
32// as big endian from `__BYTE_ORDER__`) can only be exercised by a big endian build, or by a cross
33// target such as `clang --target=s390x-linux-gnu`.
34static void verify16(void) {
35    const uint8_t littleEndianBytes[2] = { 0x22, 0x11 };
36    const uint8_t bigEndianBytes[2] = { 0x11, 0x22 };
37
38    uint16_t value;
39
40    memcpy(&value, littleEndianBytes, sizeof(value));
41    VERIFY(FF_READ_LE(value) == (uint16_t) 0x1122);
42
43    memcpy(&value, bigEndianBytes, sizeof(value));
44    VERIFY(FF_READ_BE(value) == (uint16_t) 0x1122);
45}
46
47static void verify32(void) {
48    const uint8_t littleEndianBytes[4] = { 0x44, 0x33, 0x22, 0x11 };
49    const uint8_t bigEndianBytes[4] = { 0x11, 0x22, 0x33, 0x44 };
50
51    uint32_t value;
52
53    memcpy(&value, littleEndianBytes, sizeof(value));
54    VERIFY(FF_READ_LE(value) == 0x11223344u);
55
56    memcpy(&value, bigEndianBytes, sizeof(value));
57    VERIFY(FF_READ_BE(value) == 0x11223344u);
58}
59
60static void verify64(void) {
61    const uint8_t littleEndianBytes[8] = { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
62    const uint8_t bigEndianBytes[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
63
64    uint64_t value;
65
66    memcpy(&value, littleEndianBytes, sizeof(value));
67    VERIFY(FF_READ_LE(value) == 0x1122334455667788ull);
68
69    memcpy(&value, bigEndianBytes, sizeof(value));
70    VERIFY(FF_READ_BE(value) == 0x1122334455667788ull);
71}
72
73int main(void) {
74    verify16();
75    verify32();
76    verify64();
77
78    // A byte swap has to stay inside the width of the value it was given. A value whose bytes are
79    // all equal reads the same through either macro, on either kind of machine.
80    {
81        uint16_t narrow = 0x0F0Fu;
82        VERIFY(FF_READ_LE(narrow) == (uint16_t) 0x0F0F);
83        VERIFY(FF_READ_BE(narrow) == (uint16_t) 0x0F0F);
84
85        uint32_t wide = 0x0F0F0F0Fu;
86        VERIFY(FF_READ_LE(wide) == 0x0F0F0F0Fu);
87        VERIFY(FF_READ_BE(wide) == 0x0F0F0F0Fu);
88
89        uint64_t widest = 0x0F0F0F0F0F0F0F0Full;
90        VERIFY(FF_READ_LE(widest) == 0x0F0F0F0F0F0F0F0Full);
91        VERIFY(FF_READ_BE(widest) == 0x0F0F0F0F0F0F0F0Full);
92    }
93
94    puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
95    return 0;
96}