summaryrefslogtreecommitdiff
path: root/odb/oracle/details/number.cxx
diff options
context:
space:
mode:
authorConstantin Michael <constantin@codesynthesis.com>2011-09-26 08:36:55 +0200
committerConstantin Michael <constantin@codesynthesis.com>2011-09-26 11:04:09 +0200
commit927b42789bd3c3f108867bf067a89013a932cab3 (patch)
treebcd30812c3716aeb0fb91a8afe1208bd3f37dd15 /odb/oracle/details/number.cxx
parent385a45a47fdb47a890178b7b4604a37fc214a10e (diff)
Reimplement Oracle NUMBER to/from C++ integer type conversions
The implementation has been moved to the details namespace. Signed and unsigned 32 bit versions, as well as an unsigned 64 bit version have also been added.
Diffstat (limited to 'odb/oracle/details/number.cxx')
-rw-r--r--odb/oracle/details/number.cxx264
1 files changed, 264 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;
+ }
+ }
+ }
+}