Crazy byteswapping thrift patches from david reiss

Reviewed By: ninjitsu

Test Plan: bswap64, holla holla


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665098 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/protocol/TBinaryProtocol.cpp b/lib/cpp/src/protocol/TBinaryProtocol.cpp
index 544ef88..5116e86 100644
--- a/lib/cpp/src/protocol/TBinaryProtocol.cpp
+++ b/lib/cpp/src/protocol/TBinaryProtocol.cpp
@@ -6,8 +6,50 @@
 
 #include "TBinaryProtocol.h"
 
+#include <boost/static_assert.hpp>
+
 using std::string;
 
+// Use this to get around strict aliasing rules.
+// For example, uint64_t i = bitwise_cast<uint64_t>(returns_double());
+// The most obvious implementation is to just cast a pointer,
+// but that doesn't work.
+// For a pretty in-depth explanation of the problem, see
+// http://www.cellperformance.com/mike_acton/2006/06/ (...)
+// understanding_strict_aliasing.html
+template <typename To, typename From>
+static inline To bitwise_cast(From from) {
+	BOOST_STATIC_ASSERT(sizeof(From) == sizeof(To));
+
+	// BAD!!!  These are all broken with -O2.
+	//return *reinterpret_cast<To*>(&from);  // BAD!!!
+	//return *static_cast<To*>(static_cast<void*>(&from));  // BAD!!!
+	//return *(To*)(void*)&from;  // BAD!!!
+
+	// Super clean and paritally blessed by section 3.9 of the standard.
+	//unsigned char c[sizeof(from)];
+	//memcpy(c, &from, sizeof(from));
+	//To to;
+	//memcpy(&to, c, sizeof(c));
+	//return to;
+
+	// Slightly more questionable.
+	// Same code emitted by GCC.
+	//To to;
+	//memcpy(&to, &from, sizeof(from));
+	//return to;
+
+	// Technically undefined, but almost universally supported,
+	// and the most efficient implementation.
+	union {
+		From f;
+		To t;
+	} u;
+	u.f = from;
+	return u.t;
+}
+
+
 namespace facebook { namespace thrift { namespace protocol { 
 
 uint32_t TBinaryProtocol::writeMessageBegin(const std::string& name,
@@ -113,17 +155,12 @@
 }
   
 uint32_t TBinaryProtocol::writeDouble(const double dub) {
-  uint8_t b[8];
-  uint8_t* d = (uint8_t*)&dub;
-  b[0] = d[7];
-  b[1] = d[6];
-  b[2] = d[5];
-  b[3] = d[4];
-  b[4] = d[3];
-  b[5] = d[2];
-  b[6] = d[1];
-  b[7] = d[0];
-  trans_->write((uint8_t*)b, 8);
+  BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
+  BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
+
+  uint64_t bits = bitwise_cast<uint64_t>(dub);
+  bits = htonll(bits);
+  trans_->write((uint8_t*)&bits, 8);
   return 8;
 }
 
@@ -291,18 +328,15 @@
 }
 
 uint32_t TBinaryProtocol::readDouble(double& dub) {
+  BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
+  BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
+
+  uint64_t bits;
   uint8_t b[8];
-  uint8_t d[8];
   trans_->readAll(b, 8);
-  d[0] = b[7];
-  d[1] = b[6];
-  d[2] = b[5];
-  d[3] = b[4];
-  d[4] = b[3];
-  d[5] = b[2];
-  d[6] = b[1];
-  d[7] = b[0];
-  dub = *(double*)d;
+  bits = *(uint64_t*)b;
+  bits = ntohll(bits);
+  dub = bitwise_cast<double>(bits);
   return 8;
 }