diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel index 163584d..25027e1 100644 --- a/lib/BUILD.bazel +++ b/lib/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("//bazel:copts.bzl", "COPTS") cc_library( @@ -8,8 +8,20 @@ cc_library( ], hdrs = [ "defs.h", + "endian.h", "test.h", ], copts = COPTS, visibility = ["//visibility:public"], ) + +cc_test( + name = "endian_test", + srcs = [ + "endian_test.c", + ], + copts = COPTS, + deps = [ + ":lib", + ], +) diff --git a/lib/defs.h b/lib/defs.h index 4cb7717..45df4dd 100644 --- a/lib/defs.h +++ b/lib/defs.h @@ -1,13 +1,59 @@ -#ifndef defs_h -#define defs_h +#ifndef lib_defs_h +#define lib_defs_h /* defs.h - common definitions. */ /*============================================================================== -Basic definitions +Target information ==============================================================================*/ +/* + Note that this code does not need to be especially portable. It just runs on + Mac OS and development systems (for testing). We can assume that the + development system has GCC. + + Macros we care about: + + TARGET_OS_MAC OS is some Macintosh variant (broadly speaking) + TARGET_API_MAC_OS8 Targeting classic Mac OS (9.x and earlier) + + TARGET_RT_LITTLE_ENDIAN Little-endian byte order + TARGET_RT_BIG_ENDIAN Big-endian byte order +*/ #if macintosh +/* Classic Mac OS. Header is part of Universal Interfaces & Carbon. */ +#include + +#elif __APPLE__ + +/* Newer apple systems, including macOS >= 10. Header is in /usr/include, or + within /usr/include inside the SDK. */ +#include + +#else + +#if __BYTE_ORDER__ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define TARGET_RT_LITTLE_ENDIAN 1 +#define TARGET_RT_BIG_ENDIAN 0 +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define TARGET_RT_LITTLE_ENDIAN 0 +#define TARGET_RT_BIG_ENDIAN 1 +#else +#error "unknown endian" +#endif +#else +#error "could not determine byte order" +#endif + +#endif + +/*============================================================================== +Basic types +==============================================================================*/ + +#if TARGET_API_MAC_OS8 + #include #else @@ -65,7 +111,7 @@ typedef enum Memory allocation ==============================================================================*/ -#if macintosh +#if TARGET_API_MAC_OS8 #include diff --git a/lib/endian.h b/lib/endian.h new file mode 100644 index 0000000..e95b133 --- /dev/null +++ b/lib/endian.h @@ -0,0 +1,75 @@ +#ifndef lib_endian_h +#define lib_endian_h +/* endian.h - byte order and byte swapping */ + +/* + Defines macros for swapping to and from big-endian: + + EndianU16_BtoN + EndianU16_NtoB + EndianU32_BtoN + EndianU32_NtoB +*/ + +#include "lib/defs.h" + +#if TARGET_API_MAC_OS8 + +/* Use the legacy Mac OS header, if available. */ +#include + +#else + +/* Use the compiler built-in, if available. */ +#if __clang__ + +#if __has_builtin(__builtin_bswap16) +#define Endian16_Swap(x) __builtin_bswap16(x) +#endif + +#if __has_builtin(__builtin_bswap32) +#define Endian32_Swap(x) __builtin_bswap32(x) +#endif + +#elif __GNUC__ + +#if __GNUC__ && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 3) +#define Endian16_Swap(x) __builtin_bswap16(x) +#define Endian32_Swap(x) __builtin_bswap32(x) +#endif + +#endif + +/* Fallback implementations. */ + +/* clang-format off */ +#ifndef Endian16_Swap +#define Endian16_Swap(x) \ + ((((UInt16)(x) << 8) & 0xff00) | \ + (((UInt16)(x) >> 8) & 0x00ff)) +#endif + +#ifndef Endian32_Swap +#define Endian32_Swap(x) \ + ((((UInt32)(x) << 24) & 0xff000000) | \ + (((UInt32)(x) << 8) & 0x00ff0000) | \ + (((UInt32)(x) >> 8) & 0x0000ff00) | \ + (((UInt32)(x) >> 24) & 0x000000ff)) +#endif +/* clang-format on */ + +#if TARGET_RT_BIG_ENDIAN +#define EndianU16_BtoN(x) (x) +#define EndianU16_NtoB(x) (x) +#define EndianU32_BtoN(x) (x) +#define EndianU32_NtoB(x) (x) +#else +#define EndianU16_BtoN(x) Endian16_Swap(x) +#define EndianU16_NtoB(x) Endian16_Swap(x) +#define EndianU32_BtoN(x) Endian32_Swap(x) +#define EndianU32_NtoB(x) Endian32_Swap(x) +#endif + +#endif /* TARGET_API_MAC_OS8 */ + +#endif /* lib_endian_h */ diff --git a/lib/endian_test.c b/lib/endian_test.c new file mode 100644 index 0000000..98e824f --- /dev/null +++ b/lib/endian_test.c @@ -0,0 +1,62 @@ +#include "lib/endian.h" + +#include +#include +#include + +static int gDidFail; + +static void Fail(const char *name, UInt32 got, UInt32 expect) +{ + fprintf(stderr, "%s = 0x%" PRIx32 ", expect 0x%" PRIx32 "\n", name, got, + expect); + gDidFail = 1; +} + +#define CHECK(fn, expect) \ + be = fn(u.i); \ + if (be != expect) { \ + Fail(#fn, be, expect); \ + } + +static void Test16(void) +{ + union { + UInt16 i; + UInt8 b[2]; + } u; + UInt16 be; + + u.b[0] = 0x12; + u.b[1] = 0x34; + + CHECK(EndianU16_BtoN, 0x1234) + CHECK(EndianU16_NtoB, 0x1234) +} + +static void Test32(void) +{ + union { + UInt32 i; + UInt8 b[2]; + } u; + UInt32 be; + + u.b[0] = 0x12; + u.b[1] = 0x34; + u.b[2] = 0x56; + u.b[3] = 0x78; + + CHECK(EndianU32_BtoN, 0x12345678) + CHECK(EndianU32_NtoB, 0x12345678) +} + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + Test16(); + Test32(); + return gDidFail; +}