浏览代码

Better detection of endianness and IEE754 based on the target architecture

Julius Pfrommer 6 年之前
父节点
当前提交
79aac78ed3
共有 2 个文件被更改,包括 64 次插入45 次删除
  1. 59 42
      include/ua_config.h.in
  2. 5 3
      src/ua_types_encoding_binary.c

+ 59 - 42
include/ua_config.h.in

@@ -237,91 +237,108 @@ extern "C" {
 #endif
 
 /**
- * Detect Binary Overlaying for Encoding
- * -------------------------------------
- * Integers and floating point numbers are transmitted in little-endian (IEEE 754
- * for floating point) encoding. If the target architecture uses the same
- * format, numeral datatypes can be memcpy'd (overlayed) on the binary stream.
- * This speeds up encoding.
+ * Detect Endianness and IEEE 754 floating point
+ * ---------------------------------------------
+ * Integers and floating point numbers are transmitted in little-endian (IEEE
+ * 754 for floating point) encoding. If the target architecture uses the same
+ * format, numeral datatypes can be memcpy'd (overlayed) on the network buffer.
+ * Otherwise, a slow default encoding routine is used that works for every
+ * architecture.
  *
  * Integer Endianness
  * ^^^^^^^^^^^^^^^^^^
- * The definition ``UA_BINARY_OVERLAYABLE_INTEGER`` is true when the integer
- * representation of the target architecture is little-endian. */
+ * The definition ``UA_LITTLE_ENDIAN`` is true when the integer representation
+ * of the target architecture is little-endian. */
 #if defined(_WIN32)
-# define UA_BINARY_OVERLAYABLE_INTEGER 1
+# define UA_LITTLE_ENDIAN 1
 #elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
       (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
-# define UA_BINARY_OVERLAYABLE_INTEGER 1
+# define UA_LITTLE_ENDIAN 1
 #elif defined(__linux__) /* Linux (including Android) */
 # include <endian.h>
 # if __BYTE_ORDER == __LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_INTEGER 1
+#  define UA_LITTLE_ENDIAN 1
 # endif
 #elif defined(__OpenBSD__) /* OpenBSD */
 # include <sys/endian.h>
 # if BYTE_ORDER == LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_INTEGER 1
+#  define UA_LITTLE_ENDIAN 1
 # endif
 #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) /* Other BSD */
 # include <sys/endian.h>
 # if _BYTE_ORDER == _LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_INTEGER 1
+#  define UA_LITTLE_ENDIAN 1
 # endif
 #elif defined(__APPLE__) /* Apple (MacOS, iOS) */
 # include <libkern/OSByteOrder.h>
 # if defined(__LITTLE_ENDIAN__)
-#  define UA_BINARY_OVERLAYABLE_INTEGER 1
+#  define UA_LITTLE_ENDIAN 1
 # endif
 #elif defined(__QNX__) || defined(__QNXNTO__) /* QNX */
 # include <gulliver.h>
 # if defined(__LITTLEENDIAN__)
-#  define UA_BINARY_OVERLAYABLE_INTEGER 1
+#  define UA_LITTLE_ENDIAN 1
 # endif
 #endif
-
-#ifndef UA_BINARY_OVERLAYABLE_INTEGER
-# define UA_BINARY_OVERLAYABLE_INTEGER 0
+#ifndef UA_LITTLE_ENDIAN
+# define UA_LITTLE_ENDIAN 0
 #endif
 
-/* Some platforms (e.g. QNX) have sizeof(bool) > 1. Disable overlayed integer
- * encoding if that is the case. */
-#if UA_BINARY_OVERLAYABLE_INTEGER == 1
+/* Can the integers be memcpy'd onto the network buffer? Add additional checks
+ * here. Some platforms (e.g. QNX) have sizeof(bool) > 1. Manually disable
+ * overlayed integer encoding if that is the case. */
+#if (UA_LITTLE_ENDIAN == 1)
 UA_STATIC_ASSERT(sizeof(bool) == 1, cannot_overlay_integers_with_large_bool);
+# define UA_BINARY_OVERLAYABLE_INTEGER 1
+#else
+# define UA_BINARY_OVERLAYABLE_INTEGER 0
 #endif
 
 /**
  * Float Endianness
  * ^^^^^^^^^^^^^^^^
- * The definition ``UA_BINARY_OVERLAYABLE_FLOAT`` is true when the floating
- * point number representation of the target architecture is IEEE 754. Note that
- * this cannot be reliable detected with macros for the clang compiler
- * (beginning of 2017). ``UA_BINARY_OVERLAYABLE_FLOAT`` can be manually set if
- * the target is known to be little endian with floats in the IEEE 754
- * format. */
+ * The definition ``UA_FLOAT_IEEE754`` is set to true when the floating point
+ * number representation of the target architecture is IEEE 754. The definition
+ * ``UA_FLOAT_LITTLE_ENDIAN`` is set to true when the floating point number
+ * representation is in little-endian encoding. */
 
 #if defined(_WIN32)
-# define UA_BINARY_OVERLAYABLE_FLOAT 1
+# define UA_FLOAT_IEEE754 1
+#elif defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || \
+    defined(__ia64__) || defined(__powerpc__) || defined(__sparc__) || \
+    defined(__arm__)
+# define UA_FLOAT_IEEE754 1
+#elif defined(__STDC_IEC_559__)
+# define UA_FLOAT_IEEE754 1
+#else
+# define UA_FLOAT_IEEE754 0
+#endif
+
+/* Wikipedia says (https://en.wikipedia.org/wiki/Endianness): Although the
+ * ubiquitous x86 processors of today use little-endian storage for all types of
+ * data (integer, floating point, BCD), there are a number of hardware
+ * architectures where floating-point numbers are represented in big-endian form
+ * while integers are represented in little-endian form. */
+#if defined(_WIN32)
+# define UA_FLOAT_LITTLE_ENDIAN 1
+#elif defined(__i386__) || defined(__x86_64__) || defined(__amd64__)
+# define UA_FLOAT_LITTLE_ENDIAN 1
 #elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
     (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) /* Defined only in GCC */
-# define UA_BINARY_OVERLAYABLE_FLOAT 1
+# define UA_FLOAT_LITTLE_ENDIAN 1
 #elif defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && \
     (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) /* Defined only in GCC */
-# define UA_BINARY_OVERLAYABLE_FLOAT 1
-#elif defined(__linux__) /* Linux (including Android) */
-# include <endian.h>
-# if defined(__ANDROID__)
-#  if __BYTE_ORDER == __LITTLE_ENDIAN
-#   define UA_BINARY_OVERLAYABLE_INTEGER 1
-#  endif
-# elif __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
-#  define UA_BINARY_OVERLAYABLE_FLOAT 1
-# endif
-#elif defined(_WRS_KERNEL)
-# define UA_BINARY_OVERLAYABLE_FLOAT 1
+# define UA_FLOAT_LITTLE_ENDIAN 1
+#endif
+#ifndef UA_FLOAT_LITTLE_ENDIAN
+# define UA_FLOAT_LITTLE_ENDIAN 0
 #endif
 
-#ifndef UA_BINARY_OVERLAYABLE_FLOAT
+/* Only if the floating points are litle-endian **and** in IEEE 754 format can
+ * we memcpy directly onto the network buffer. */
+#if (UA_FLOAT_IEEE754 == 1) && (UA_FLOAT_LITTLE_ENDIAN == 1)
+# define UA_BINARY_OVERLAYABLE_FLOAT 1
+#else
 # define UA_BINARY_OVERLAYABLE_FLOAT 0
 #endif
 

+ 5 - 3
src/ua_types_encoding_binary.c

@@ -131,7 +131,7 @@ encodeWithExchangeBuffer(const void *ptr, encodeBinarySignature encodeFunc, Ctx
 
 #if !UA_BINARY_OVERLAYABLE_INTEGER
 
-#pragma message "Integer endianness could not be detected to be little endian. Use slow generic encoding."
+#warning "Integer endianness could not be detected to be little endian. Use slow generic encoding."
 
 /* These en/decoding functions are only used when the architecture isn't little-endian. */
 static void
@@ -296,7 +296,9 @@ DECODE_BINARY(UInt64) {
 /* Floating Point Types */
 /************************/
 
-#if UA_BINARY_OVERLAYABLE_FLOAT
+/* Can we reuse the integer encoding mechanism by casting floating point
+ * values? */
+#if (UA_FLOAT_IEEE754 == 1) && (UA_LITTLE_ENDIAN == UA_FLOAT_LITTLE_ENDIAN)
 # define Float_encodeBinary UInt32_encodeBinary
 # define Float_decodeBinary UInt32_decodeBinary
 # define Double_encodeBinary UInt64_encodeBinary
@@ -305,7 +307,7 @@ DECODE_BINARY(UInt64) {
 
 #include <math.h>
 
-#pragma message "No native IEEE 754 format detected. Use slow generic encoding."
+#warning "No native IEEE 754 format detected. Use slow generic encoding."
 
 /* Handling of IEEE754 floating point values was taken from Beej's Guide to
  * Network Programming (http://beej.us/guide/bgnet/) and enhanced to cover the