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}