aboutsummaryrefslogtreecommitdiff
path: root/odb/oracle/details
diff options
context:
space:
mode:
Diffstat (limited to 'odb/oracle/details')
-rw-r--r--odb/oracle/details/number.cxx264
-rw-r--r--odb/oracle/details/number.hxx35
2 files changed, 299 insertions, 0 deletions
diff --git a/odb/oracle/details/number.cxx b/odb/oracle/details/number.cxx
new file mode 100644
index 0000000..bf7902a
--- /dev/null
+++ b/odb/oracle/details/number.cxx
@@ -0,0 +1,264 @@
+// file : odb/oracle/details/number.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/oracle/details/number.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ // The VARNUM type's binary representation is made up of the following
+ // bit fields, ordered in increasing memory address.
+ //
+ // 000 to 007: The size of the binary representation, including the
+ // exponent bits and the mantissa bits, but excluding these
+ // length bits.
+ //
+ // 008 to 015: The base-100 exponent bits. The most significant bit is
+ // the sign bit of the number, which is set for positive
+ // numbers and cleared for negative numbers. For positive
+ // numbers, the exponent has a bias of 65 added to it.
+ //
+ // 016 to 175: The mantissa bits. Each byte of this field represents a
+ // single base-100 value.
+ //
+ //
+
+ long long
+ number_to_int64 (const unsigned char* b)
+ {
+ int n (b[0]);
+
+ // Zero is represented by zero significant bits and an exponent
+ // set to 128.
+ //
+ if (n == 1)
+ {
+ assert (b[1] == 128);
+ return 0;
+ }
+
+ long long v (0);
+
+ // Test the sign bit of the exponent.
+ //
+ if (b[1] & 0x80)
+ {
+
+ // The unbiased exponent of a positive number may be calculated as
+ // b[1] - 128 - 65. For an integer, this is the order of magnitude
+ // of the number. Calculate the maximum weight, 100 ^ o, where o is
+ // the order of magnitude of the number.
+ //
+ long long w (1);
+
+ for (size_t i (0), o (b[1] - 193); i < o; ++i)
+ w *= 100;
+
+ // Accumlate the sum of the significant base-100 terms.
+ //
+ for (const unsigned char* m (b + 2), *e (b + 1 + n); m < e; ++m)
+ {
+ v += (*m - 1) * w;
+ w /= 100;
+ }
+ }
+ else
+ {
+ // The unbiased exponent of a negative number is calculated as
+ // (~b[1] & 0x7F) - 193. For an integer, this is the order of
+ // magnitude of the number. Calculate the maximum weight, 100 ^ o,
+ // where o is the order of magnitude of the number.
+ //
+ long long w (1);
+
+ for (size_t i (0), o ((~b[1] & 0x7F) - 65); i < o; ++i)
+ w *= 100;
+
+ // Accumulate the sum of the significant base-100 terms. Note that
+ // negative values will have a terminator byte which is included
+ // in the length. This is ignored.
+ //
+ for (const unsigned char* m (b + 2), *e (b + n); m < e; ++m)
+ {
+ v -= (101 - *m) * w;
+ w /= 100;
+ }
+ }
+
+ return v;
+ }
+
+ void
+ int64_to_number (unsigned char* b, long long v)
+ {
+ // We assume that b is long enough to contain a long long VARNUM
+ // representation, that being 12 bytes.
+ //
+
+ if (v == 0)
+ {
+ b[0] = 1;
+ b[1] = 128;
+
+ return;
+ }
+
+ bool sig (false);
+ size_t n (0);
+ unsigned char t[11], *m (t);
+
+ if (v < 0)
+ {
+ // Termination marker for negative numbers.
+ //
+ *m++ = 102;
+
+ while (v != 0)
+ {
+ int r (static_cast<int> (v % 100));
+ sig = sig || r != 0;
+
+ if (sig)
+ *m++ = static_cast<unsigned char> (101 + r);
+
+ v /= 100;
+ ++n;
+ }
+
+ // The exponent is one less than the number of base 100 digits. It is
+ // inverted for negative values.
+ //
+ b[1] = static_cast<unsigned char> (~(n + 192));
+ }
+ else
+ {
+ while (v != 0)
+ {
+ int r (static_cast<int> (v % 100));
+ sig = sig || r != 0;
+
+ if (sig)
+ *m++ = static_cast<unsigned char> (r + 1);
+
+ v /= 100;
+ ++n;
+ }
+
+ // Exponent is one less than the number of base 100 digits.
+ //
+ b[1] = static_cast<unsigned char> (n + 192);
+ }
+
+ // Set the length.
+ //
+ b[0] = static_cast<unsigned char> (m - t + 1);
+
+ // Set the significant digits in big-endian byte order and the
+ // terminator, if any.
+ //
+ for (size_t i (2); m > t; ++i)
+ b[i] = *--m;
+ }
+
+ unsigned long long
+ number_to_uint64 (const unsigned char* b)
+ {
+ int n (b[0]);
+
+ // Zero is represented by zero significant bits and an exponent
+ // set to 128.
+ //
+ if (n == 1)
+ {
+ assert (b[1] == 128);
+ return 0;
+ }
+
+ unsigned long long v (0);
+
+ // Test the sign bit of the exponent.
+ //
+ if (b[1] & 0x80)
+ {
+ assert (false);
+ return 0;
+ }
+
+ // The unbiased exponent of a positive number may be calculated as
+ // b[1] - 128 - 65. For an integer, this is the order of magnitude
+ // of the number. Calculate the maximum weight, 100 ^ o, where o is
+ // the order of magnitude of the number.
+ //
+ unsigned long long w (1);
+
+ for (size_t i (0), o (b[1] - 193); i < o; ++i)
+ w *= 100;
+
+ // Accumlate the sum of the significant base-100 terms.
+ //
+ for (const unsigned char* m (b + 2), *e (b + 1 + n); m < e; ++m)
+ {
+ v += (*m - 1) * w;
+ w /= 100;
+ }
+
+ return v;
+ }
+
+ void
+ uint64_to_number (unsigned char* b, unsigned long long v)
+ {
+ // We assume that b is long enough to contain an unsigned long long
+ // VARNUM representation, that being 12 bytes.
+ //
+
+ if (v == 0)
+ {
+ b[0] = 1;
+ b[1] = 128;
+
+ return;
+ }
+
+ bool sig (false);
+ size_t n (0);
+ unsigned char t[11], *m (t);
+
+ while (v != 0)
+ {
+ int r (static_cast<int> (v % 100));
+ sig = sig || r != 0;
+
+ if (sig)
+ *m++ = static_cast<unsigned char> (r + 1);
+
+ v /= 100;
+ ++n;
+ }
+
+ // Exponent is one less than the number of base 100 digits.
+ //
+ b[1] = static_cast<unsigned char> (n + 192);
+
+ // Set the length.
+ //
+ b[0] = static_cast<unsigned char> (m - t + 1);
+
+ // Set the significant digits in big-endian byte order.
+ //
+ for (size_t i (2); m > t; ++i)
+ b[i] = *--m;
+ }
+ }
+ }
+}
diff --git a/odb/oracle/details/number.hxx b/odb/oracle/details/number.hxx
new file mode 100644
index 0000000..4f49643
--- /dev/null
+++ b/odb/oracle/details/number.hxx
@@ -0,0 +1,35 @@
+// file : odb/oracle/details/number.hxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef ODB_ORACLE_NUMBER_HXX
+#define ODB_ORACLE_NUMBER_HXX
+
+#include <odb/pre.hxx>
+#include <odb/oracle/details/export.hxx>
+
+namespace odb
+{
+ namespace oracle
+ {
+ namespace details
+ {
+ LIBODB_ORACLE_EXPORT long long
+ number_to_int64 (const unsigned char* buffer);
+
+ LIBODB_ORACLE_EXPORT void
+ int64_to_number (unsigned char* buffer, long long value);
+
+ LIBODB_ORACLE_EXPORT unsigned long long
+ number_to_uint64 (const unsigned char* buffer);
+
+ LIBODB_ORACLE_EXPORT void
+ uint64_to_number (unsigned char* buffer, unsigned long long value);
+ }
+ }
+}
+
+#include <odb/post.hxx>
+
+#endif // ODB_ORACLE_NUMBER_HXX