/* * Utilities to deal with numbers. * * Copyright (C) 2020 The Psycopg Team * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California */ #include #include #include "pg_config.h" /* * 64-bit integers */ typedef uint64_t uint64; typedef int64_t int64; #define UINT64CONST(x) UINT64_C(x) #if !defined(HAVE__BUILTIN_CLZ) || (SIZEOF_LONG != 8 && SIZEOF_LONG_LONG != 8) static const uint8_t pg_leftmost_one_pos[256] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; #endif static const char DIGIT_TABLE[200] = { '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9' }; /* * pg_leftmost_one_pos64 * Returns the position of the most significant set bit in "word", * measured from the least significant bit. word must not be 0. */ static inline int pg_leftmost_one_pos64(uint64_t word) { #if defined(HAVE__BUILTIN_CLZ) && SIZEOF_LONG == 8 return 63 - __builtin_clzl(word); #elif defined(HAVE__BUILTIN_CLZ) && SIZEOF_LONG_LONG == 8 return 63 - __builtin_clzll(word); #else int shift = 64 - 8; while ((word >> shift) == 0) shift -= 8; return shift + pg_leftmost_one_pos[(word >> shift) & 255]; #endif } static inline int decimalLength64(const uint64_t v) { int t; static const uint64_t PowersOfTen[] = { UINT64CONST(1), UINT64CONST(10), UINT64CONST(100), UINT64CONST(1000), UINT64CONST(10000), UINT64CONST(100000), UINT64CONST(1000000), UINT64CONST(10000000), UINT64CONST(100000000), UINT64CONST(1000000000), UINT64CONST(10000000000), UINT64CONST(100000000000), UINT64CONST(1000000000000), UINT64CONST(10000000000000), UINT64CONST(100000000000000), UINT64CONST(1000000000000000), UINT64CONST(10000000000000000), UINT64CONST(100000000000000000), UINT64CONST(1000000000000000000), UINT64CONST(10000000000000000000) }; /* * Compute base-10 logarithm by dividing the base-2 logarithm by a * good-enough approximation of the base-2 logarithm of 10 */ t = (pg_leftmost_one_pos64(v) + 1) * 1233 / 4096; return t + (v >= PowersOfTen[t]); } /* * Get the decimal representation, not NUL-terminated, and return the length of * same. Caller must ensure that a points to at least MAXINT8LEN bytes. */ int pg_ulltoa_n(uint64_t value, char *a) { int olength, i = 0; uint32_t value2; /* Degenerate case */ if (value == 0) { *a = '0'; return 1; } olength = decimalLength64(value); /* Compute the result string. */ while (value >= 100000000) { const uint64_t q = value / 100000000; uint32_t value2 = (uint32_t) (value - 100000000 * q); const uint32_t c = value2 % 10000; const uint32_t d = value2 / 10000; const uint32_t c0 = (c % 100) << 1; const uint32_t c1 = (c / 100) << 1; const uint32_t d0 = (d % 100) << 1; const uint32_t d1 = (d / 100) << 1; char *pos = a + olength - i; value = q; memcpy(pos - 2, DIGIT_TABLE + c0, 2); memcpy(pos - 4, DIGIT_TABLE + c1, 2); memcpy(pos - 6, DIGIT_TABLE + d0, 2); memcpy(pos - 8, DIGIT_TABLE + d1, 2); i += 8; } /* Switch to 32-bit for speed */ value2 = (uint32_t) value; if (value2 >= 10000) { const uint32_t c = value2 - 10000 * (value2 / 10000); const uint32_t c0 = (c % 100) << 1; const uint32_t c1 = (c / 100) << 1; char *pos = a + olength - i; value2 /= 10000; memcpy(pos - 2, DIGIT_TABLE + c0, 2); memcpy(pos - 4, DIGIT_TABLE + c1, 2); i += 4; } if (value2 >= 100) { const uint32_t c = (value2 % 100) << 1; char *pos = a + olength - i; value2 /= 100; memcpy(pos - 2, DIGIT_TABLE + c, 2); i += 2; } if (value2 >= 10) { const uint32_t c = value2 << 1; char *pos = a + olength - i; memcpy(pos - 2, DIGIT_TABLE + c, 2); } else *a = (char) ('0' + value2); return olength; } /* * pg_lltoa: converts a signed 64-bit integer to its string representation and * returns strlen(a). * * Caller must ensure that 'a' points to enough memory to hold the result * (at least MAXINT8LEN + 1 bytes, counting a leading sign and trailing NUL). */ int pg_lltoa(int64_t value, char *a) { uint64_t uvalue = value; int len = 0; if (value < 0) { uvalue = (uint64_t) 0 - uvalue; a[len++] = '-'; } len += pg_ulltoa_n(uvalue, a + len); a[len] = '\0'; return len; }