Header And Logo

PostgreSQL
| The world's most advanced open source database.

numutils.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * numutils.c
00004  *    utility functions for I/O of built-in numeric types.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/utils/adt/numutils.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <math.h>
00018 #include <limits.h>
00019 #include <ctype.h>
00020 
00021 #include "utils/builtins.h"
00022 
00023 /*
00024  * pg_atoi: convert string to integer
00025  *
00026  * allows any number of leading or trailing whitespace characters.
00027  *
00028  * 'size' is the sizeof() the desired integral result (1, 2, or 4 bytes).
00029  *
00030  * c, if not 0, is a terminator character that may appear after the
00031  * integer (plus whitespace).  If 0, the string must end after the integer.
00032  *
00033  * Unlike plain atoi(), this will throw ereport() upon bad input format or
00034  * overflow.
00035  */
00036 int32
00037 pg_atoi(char *s, int size, int c)
00038 {
00039     long        l;
00040     char       *badp;
00041 
00042     /*
00043      * Some versions of strtol treat the empty string as an error, but some
00044      * seem not to.  Make an explicit test to be sure we catch it.
00045      */
00046     if (s == NULL)
00047         elog(ERROR, "NULL pointer");
00048     if (*s == 0)
00049         ereport(ERROR,
00050                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00051                  errmsg("invalid input syntax for integer: \"%s\"",
00052                         s)));
00053 
00054     errno = 0;
00055     l = strtol(s, &badp, 10);
00056 
00057     /* We made no progress parsing the string, so bail out */
00058     if (s == badp)
00059         ereport(ERROR,
00060                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00061                  errmsg("invalid input syntax for integer: \"%s\"",
00062                         s)));
00063 
00064     switch (size)
00065     {
00066         case sizeof(int32):
00067             if (errno == ERANGE
00068 #if defined(HAVE_LONG_INT_64)
00069             /* won't get ERANGE on these with 64-bit longs... */
00070                 || l < INT_MIN || l > INT_MAX
00071 #endif
00072                 )
00073                 ereport(ERROR,
00074                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00075                 errmsg("value \"%s\" is out of range for type integer", s)));
00076             break;
00077         case sizeof(int16):
00078             if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
00079                 ereport(ERROR,
00080                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00081                 errmsg("value \"%s\" is out of range for type smallint", s)));
00082             break;
00083         case sizeof(int8):
00084             if (errno == ERANGE || l < SCHAR_MIN || l > SCHAR_MAX)
00085                 ereport(ERROR,
00086                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00087                 errmsg("value \"%s\" is out of range for 8-bit integer", s)));
00088             break;
00089         default:
00090             elog(ERROR, "unsupported result size: %d", size);
00091     }
00092 
00093     /*
00094      * Skip any trailing whitespace; if anything but whitespace remains before
00095      * the terminating character, bail out
00096      */
00097     while (*badp && *badp != c && isspace((unsigned char) *badp))
00098         badp++;
00099 
00100     if (*badp && *badp != c)
00101         ereport(ERROR,
00102                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00103                  errmsg("invalid input syntax for integer: \"%s\"",
00104                         s)));
00105 
00106     return (int32) l;
00107 }
00108 
00109 /*
00110  * pg_itoa: converts a signed 16-bit integer to its string representation
00111  *
00112  * Caller must ensure that 'a' points to enough memory to hold the result
00113  * (at least 7 bytes, counting a leading sign and trailing NUL).
00114  *
00115  * It doesn't seem worth implementing this separately.
00116  */
00117 void
00118 pg_itoa(int16 i, char *a)
00119 {
00120     pg_ltoa((int32) i, a);
00121 }
00122 
00123 /*
00124  * pg_ltoa: converts a signed 32-bit integer to its string representation
00125  *
00126  * Caller must ensure that 'a' points to enough memory to hold the result
00127  * (at least 12 bytes, counting a leading sign and trailing NUL).
00128  */
00129 void
00130 pg_ltoa(int32 value, char *a)
00131 {
00132     char       *start = a;
00133     bool        neg = false;
00134 
00135     /*
00136      * Avoid problems with the most negative integer not being representable
00137      * as a positive integer.
00138      */
00139     if (value == (-2147483647 - 1))
00140     {
00141         memcpy(a, "-2147483648", 12);
00142         return;
00143     }
00144     else if (value < 0)
00145     {
00146         value = -value;
00147         neg = true;
00148     }
00149 
00150     /* Compute the result string backwards. */
00151     do
00152     {
00153         int32       remainder;
00154         int32       oldval = value;
00155 
00156         value /= 10;
00157         remainder = oldval - value * 10;
00158         *a++ = '0' + remainder;
00159     } while (value != 0);
00160 
00161     if (neg)
00162         *a++ = '-';
00163 
00164     /* Add trailing NUL byte, and back up 'a' to the last character. */
00165     *a-- = '\0';
00166 
00167     /* Reverse string. */
00168     while (start < a)
00169     {
00170         char        swap = *start;
00171 
00172         *start++ = *a;
00173         *a-- = swap;
00174     }
00175 }
00176 
00177 /*
00178  * pg_lltoa: convert a signed 64-bit integer to its string representation
00179  *
00180  * Caller must ensure that 'a' points to enough memory to hold the result
00181  * (at least MAXINT8LEN+1 bytes, counting a leading sign and trailing NUL).
00182  */
00183 void
00184 pg_lltoa(int64 value, char *a)
00185 {
00186     char       *start = a;
00187     bool        neg = false;
00188 
00189     /*
00190      * Avoid problems with the most negative integer not being representable
00191      * as a positive integer.
00192      */
00193     if (value == (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1))
00194     {
00195         memcpy(a, "-9223372036854775808", 21);
00196         return;
00197     }
00198     else if (value < 0)
00199     {
00200         value = -value;
00201         neg = true;
00202     }
00203 
00204     /* Compute the result string backwards. */
00205     do
00206     {
00207         int64       remainder;
00208         int64       oldval = value;
00209 
00210         value /= 10;
00211         remainder = oldval - value * 10;
00212         *a++ = '0' + remainder;
00213     } while (value != 0);
00214 
00215     if (neg)
00216         *a++ = '-';
00217 
00218     /* Add trailing NUL byte, and back up 'a' to the last character. */
00219     *a-- = '\0';
00220 
00221     /* Reverse string. */
00222     while (start < a)
00223     {
00224         char        swap = *start;
00225 
00226         *start++ = *a;
00227         *a-- = swap;
00228     }
00229 }