Date: Wednesday, January 25, 2006 @ 23:59:02
  Author: marc
    Path: /cvsroot/carob/carob

Modified: include/SQLDataSerialization.hpp (1.9 -> 1.10)
          src/SQLDataSerialization.cpp (1.17 -> 1.18)

CAROB-31: reworked deserialization of floats and doubles, should be
really portable now: no more "union casting" + sanity checks on IEEE
754 binary format and endianness at start-up time. Needs testing on
big endian arch.


----------------------------------+
 include/SQLDataSerialization.hpp |   29 +++++
 src/SQLDataSerialization.cpp     |  198 ++++++++++++++++++++++++++++++-------
 2 files changed, 192 insertions(+), 35 deletions(-)


Index: carob/include/SQLDataSerialization.hpp
diff -u carob/include/SQLDataSerialization.hpp:1.9 
carob/include/SQLDataSerialization.hpp:1.10
--- carob/include/SQLDataSerialization.hpp:1.9  Wed Jan 25 23:10:13 2006
+++ carob/include/SQLDataSerialization.hpp      Wed Jan 25 23:59:02 2006
@@ -16,7 +16,7 @@
  * limitations under the License.
  *
  * Initial developer(s): Gilles Rayrat
- * Contributor(s): 
+ * Contributor(s): Marc Herbert
  */
 
 #ifndef SQLDATASERIALIZATION_H_
@@ -48,6 +48,33 @@
    */
   static deserializerPtr getDeserializer(TypeTag ttPrm)
       throw (NotImplementedException, UnexpectedException);
+
+  /**
+   * Relatively safe "reinterpret_cast" of an IEEE 754 float to its
+   * uint32_t bit representation. Does NOT swap bytes in any way, so
+   * endianness of the result is arch-specific. Endianness of floats
+   * is typically the same as for integer types, but not granted.
+   */
+  static uint32_t
+  floatToU32Bits(float f);
+
+  /** See floatToU32Bits(float f) */
+  static float
+  U32BitsToFloat(uint32_t ui);
+
+  /**
+   * Relatively safe "reinterpret_cast" of an IEEE 754 double to its
+   * uint64_t bit representation. Does NOT swap bytes in any way, so
+   * endianness of the result is arch-specific. Endianness of doubles
+   * is typically the same as for integer types, but not granted.
+   */
+  static uint64_t
+  doubleToU64Bits(double f);
+
+  /** See doubleToU64Bits(double d) */
+  static double
+  U64BitsToDouble(uint64_t ui);
+
 };
 
 /**
Index: carob/src/SQLDataSerialization.cpp
diff -u carob/src/SQLDataSerialization.cpp:1.17 
carob/src/SQLDataSerialization.cpp:1.18
--- carob/src/SQLDataSerialization.cpp:1.17     Wed Jan 25 23:05:25 2006
+++ carob/src/SQLDataSerialization.cpp  Wed Jan 25 23:59:02 2006
@@ -16,7 +16,7 @@
  * limitations under the License.
  *
  * Initial developer(s): Gilles Rayrat
- * Contributor(s): 
+ * Contributor(s): Marc Herbert
  */
 
 #include "SQLDataSerialization.hpp"
@@ -38,13 +38,22 @@
 #ifndef __STDC_IEC_559__
 #error only IEEE 754 platforms are supported
 #endif
- // also available at run-time: numeric_limits<double|float>.is_iec559()
+ // run-time equivalent is: numeric_limits<double|float>.is_iec559()
 
+#if (CHAR_BIT != 8)
+#error CHAR_BIT != 8 is not supported
+#endif
 
 using std::wstring;
 
 using namespace CarobNS;
 
+namespace {
+    // forward
+    extern const bool floats_inverted_endianness;
+}
+
+
 // String
 ResultSetDataType stringDeserializer(const DriverSocket& input)
     throw (SocketIOException, UnexpectedException)
@@ -93,6 +102,27 @@
 }
 
 // Float
+
+uint32_t
+SQLDataSerialization::floatToU32Bits(float f)
+{
+    const unsigned char *bytes = reinterpret_cast<const unsigned char *>(&f);
+    uint32_t ires = 0;
+    for (int c=0, shift=0; c<4; c++, shift+=8)
+        ires |= (uint32_t) bytes[c] << shift;
+    return ires;
+}
+
+float
+SQLDataSerialization::U32BitsToFloat(const uint32_t ui)
+{
+    float res;
+    unsigned char *bytes = reinterpret_cast<unsigned char *>(&res);
+    for (int c=0, shift=0; c<4; c++, shift+=8)
+        bytes[c] = ui >> shift;
+    return res;
+}
+
 /**
  * Converts the input integer which is in in IEEE 754 floating-point
  * "single format" bit layout to the corresponding float. 
@@ -105,28 +135,45 @@
     throw (SocketIOException, UnexpectedException)
 {
   ResultSetDataType res;
+  int32_t intRead;
 
-#if 1
-  input >> res.as_int;
-#else
-
-  int32_t intRead = 0;
-
-  //Receive the float as an integer (intbits)
-  input>>intRead;
-// OTHER IMPLEM: // buggy? see CAROB-31
-  int s = ((intRead & 0x80000000) == 0) ? 1 : -1;
-  int e = ((intRead & 0x7f800000) >> 23);
-  int m =  (intRead & 0x007fffff);
-  m |= 0x00800000;
-  res.as_float = (float)s * (float)m * (float)pow(2, e-1075);
-
-#endif
+  input >> intRead;  // this does call ntohl()
+  
+  // "union casting" { float; uint32_t; } is not allowed because of aliasing 
rules
+  // alignement issues, etc.
+  // See discussion thread "reinterpret_cast to extract bits" thread in 
comp.lang.c++
+  // in may 2004
+  // 
http://groups.google.fr/group/comp.std.c++/browse_frm/thread/272f1199322af93d/7d3368013656d923
+  if (floats_inverted_endianness)
+      // obviously cannot use ntohl() to swap unconditionnally!
+      throw NotImplementedException(L"Inverted endianness Not Implemented 
Yet");
+  else
+      res.as_float = SQLDataSerialization::U32BitsToFloat(intRead); // sign 
casting here
 
   return res;
 }
 
 // Double
+uint64_t
+SQLDataSerialization::doubleToU64Bits(double d)
+{
+    uint64_t ires = 0;
+    const unsigned char *bytes = reinterpret_cast<const unsigned char *>(&d);
+    for (int c=0, shift=0; c<8; c++, shift+=8)
+        ires |= (uint64_t) bytes[c] << shift;
+    return ires;
+}
+
+double
+SQLDataSerialization::U64BitsToDouble(const uint64_t ui)
+{
+    double res;
+    unsigned char *bytes = reinterpret_cast<unsigned char *>(&res);
+    for (int c=0, shift=0; c<8; c++, shift+=8)
+        bytes[c] = ui >> shift;
+    return res;
+}
+
 /**
  * Converts the argument in IEEE 754 floating-point "double format" bit layout
  * to the corresponding float. Bit 63 (the most significant) is the sign bit,
@@ -137,24 +184,17 @@
 ResultSetDataType doubleDeserializer(const DriverSocket& input)
     throw (SocketIOException, UnexpectedException)
 {
+  // code duplicated with floatDeserializer (sorry, no template)
+  // -> go and see the numerous implementation comments there
   ResultSetDataType res;
+  int64_t intRead;
 
-#if 1 // see CAROB-31
-  input >> res.as_long;
-#else
-
-  int64_t longRead = 0;
-
-  //Receive the float as an integer (intbits)
-  input>>longRead;
-
-  long s = ((longRead & 0x8000000000000000LL) == 0) ? 1 : -1;
-  long e = ((longRead & 0x7ff0000000000000LL) >> 23);
-  long m =  (longRead & 0x000fffffffffffffLL);
-  m |= 0x0010000000000000LL;
-  res.as_double = (double)s * (double)m * (double)pow(2, e-1075);
+  input >> intRead; // this does call ntohll()
 
-#endif
+  if (floats_inverted_endianness)
+      throw NotImplementedException(L"Inverted endianness Not Implemented 
Yet");
+  else
+      res.as_double = SQLDataSerialization::U64BitsToDouble(intRead); // sign 
casting here
 
   return res;
 }
@@ -274,10 +314,100 @@
   }
 }
 
+namespace {
+
+// shortcuts
+#define F2I(f) SQLDataSerialization::floatToU32Bits(f)
+#define D2I(d) SQLDataSerialization::doubleToU64Bits(d);
+
+bool
+floats_little_endianness()
+{ 
+
+    // build some magical float constants
+    
+    // e125f = 2^-125, so we have: s = 0, e = 2, m = 0 (first '1' in m
+    // is implicit). So that's just one bit set to '1' in the higher
+    // (s/e) byte, and all the rest is '0'
+    float e125f_ = 1;
+    for (int i=0; i < 5; i++)
+        e125f_ /= (1 << 25);
+    // insert a '1' in the digit before the last
+    float e125fbis_ = e125f_ + e125f_ / (1 << 22);
+
+    uint32_t e125f = F2I(e125f_);
+    uint32_t e125fbis = F2I(e125fbis_);
+
+
+    // build some magical double constants
+
+    // e1007d = 2^-1007, and same conclusion as e125f above
+    double e1007d_ = 1;
+    for (int i=0; i < 53; i++)
+        e1007d_ /= (1 << 19);
+    // insert a '1' in the digit before the last
+    double e1007dbis_ = e1007d_ + e1007d_ / ((uint64_t) 1 << 51);
+
+    uint64_t e1007d = D2I(e1007d_);
+    uint64_t e1007dbis = D2I(e1007dbis_);
+
+    // then check if we have an classical MSB-float arch
+    // (sign, exponent, fraction)
+    // UNTESTED
+    if (reinterpret_cast<unsigned char*>(&e125f)[0] == 1
+        && reinterpret_cast<unsigned char*>(&e125fbis)[3] == 2
+        && reinterpret_cast<unsigned char*>(&e1007d)[0] == 1
+        && reinterpret_cast<unsigned char*>(&e1007dbis)[7] == 2)
+        return false;
+
+    // else check if we have an classical LSB-float arch
+    // TESTED (on IA32)
+    if (reinterpret_cast<unsigned char*>(&e125f)[3] == 1
+        && reinterpret_cast<unsigned char*>(&e125fbis)[0] == 2
+        && reinterpret_cast<unsigned char*>(&e1007d)[7] == 1
+        && reinterpret_cast<unsigned char*>(&e1007dbis)[0] == 2)
+        return true;
+
+    // else weird unsupported arch, abort
+    throw NotImplementedException(L"Architecture using an unsupported floating 
point endianness");
+}
+
+bool
+ints_little_endianness()
+{
+    uint32_t i = 0x01000002U;
+    uint64_t l = 0x0100000000000002ULL;
+
+    // then check if we have an classical MSB-float arch
+    // (sign, exponent, fraction)
+    // UNTESTED
+    if (reinterpret_cast<unsigned char*>(&i)[0] == 1
+        && reinterpret_cast<unsigned char*>(&i)[3] == 2
+        && reinterpret_cast<unsigned char*>(&l)[0] == 1
+        && reinterpret_cast<unsigned char*>(&l)[7] == 2)
+        return false;
+
+    // else check if we have an classical LSB-float arch
+    // TESTED (on IA32)
+    if (reinterpret_cast<unsigned char*>(&i)[3] == 1
+        && reinterpret_cast<unsigned char*>(&i)[0] == 2
+        && reinterpret_cast<unsigned char*>(&l)[7] == 1
+        && reinterpret_cast<unsigned char*>(&l)[0] == 2)
+        return true;
+
+    // else weird unsupported arch, abort
+    throw NotImplementedException(L"Architecture using an unsupported integer 
endianness");
+}
+
+const bool floats_inverted_endianness = floats_little_endianness() ^ 
ints_little_endianness();
+
+} // unnamed namespace
+
+
 /*
  * Local Variables:
  * c-file-style: "bsd"
- * c-basic-offset: 2
+ * c-basic-offset: 4
  * indent-tabs-mode: nil
  * End:
  */

_______________________________________________
Carob-commits mailing list
[email protected]
https://forge.continuent.org/mailman/listinfo/carob-commits

Reply via email to