]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/numutils.c
Run pgindent on 9.2 source tree in preparation for first 9.3
[postgresql] / src / backend / utils / adt / numutils.c
index db253172b2f0131cb72445aa5455e7d9d92401e2..f98e7f1020eb0d6f797875af69ee955e125d7552 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * numutils.c--
- *    utility functions for I/O of built-in numeric types.
+ * numutils.c
+ *       utility functions for I/O of built-in numeric types.
  *
- *     integer:                itoa, ltoa
- *     floating point:         ftoa, atof1
- *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/utils/adt/numutils.c,v 1.7 1996/11/27 08:15:54 bryanh Exp $
+ *       src/backend/utils/adt/numutils.c
  *
  *-------------------------------------------------------------------------
  */
-#include <stdio.h>             /* for sprintf() */
-#include <errno.h>
-#include <math.h>
 #include "postgres.h"
-#include "utils/builtins.h"            /* where the declarations go */
-#ifndef HAVE_MEMMOVE
-# include <regex/utils.h>
-#else
-# include <string.h>
-#endif
-#include <port-protos.h> /* ecvt(), fcvt() */
 
+#include <math.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "utils/builtins.h"
+
+/*
+ * pg_atoi: convert string to integer
+ *
+ * allows any number of leading or trailing whitespace characters.
+ *
+ * 'size' is the sizeof() the desired integral result (1, 2, or 4 bytes).
+ *
+ * c, if not 0, is a terminator character that may appear after the
+ * integer (plus whitespace).  If 0, the string must end after the integer.
+ *
+ * Unlike plain atoi(), this will throw ereport() upon bad input format or
+ * overflow.
+ */
 int32
 pg_atoi(char *s, int size, int c)
 {
-    long l;
-    char *badp = (char *) NULL;
-    
-    Assert(s);
-    
-    errno = 0;
-    l = strtol(s, &badp, 10);
-    if (errno)         /* strtol must set ERANGE */
-       elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
-    if (badp && *badp && (*badp != c))
-       elog(WARN, "pg_atoi: error in \"%s\": can\'t parse \"%s\"", s, badp);
-    
-    switch (size) {
-    case sizeof(int32):
-#ifdef HAS_LONG_LONG
-       /* won't get ERANGE on these with 64-bit longs... */
-       if (l < -0x80000000L) {
-           errno = ERANGE;
-           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
-       }
-       if (l > 0x7fffffffL) {
-           errno = ERANGE;
-           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
-       }
-#endif /* HAS_LONG_LONG */
-       break;
-    case sizeof(int16):
-       if (l < -0x8000) {
-           errno = ERANGE;
-           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
-       }
-       if (l > 0x7fff) {
-           errno = ERANGE;
-           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
-       }
-       break;
-    case sizeof(int8):
-       if (l < -0x80) {
-           errno = ERANGE;
-           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
-       }
-       if (l > 0x7f) {
-           errno = ERANGE;
-           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       long            l;
+       char       *badp;
+
+       /*
+        * Some versions of strtol treat the empty string as an error, but some
+        * seem not to.  Make an explicit test to be sure we catch it.
+        */
+       if (s == NULL)
+               elog(ERROR, "NULL pointer");
+       if (*s == 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for integer: \"%s\"",
+                                               s)));
+
+       errno = 0;
+       l = strtol(s, &badp, 10);
+
+       /* We made no progress parsing the string, so bail out */
+       if (s == badp)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for integer: \"%s\"",
+                                               s)));
+
+       switch (size)
+       {
+               case sizeof(int32):
+                       if (errno == ERANGE
+#if defined(HAVE_LONG_INT_64)
+                       /* won't get ERANGE on these with 64-bit longs... */
+                               || l < INT_MIN || l > INT_MAX
+#endif
+                               )
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                               errmsg("value \"%s\" is out of range for type integer", s)));
+                       break;
+               case sizeof(int16):
+                       if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                               errmsg("value \"%s\" is out of range for type smallint", s)));
+                       break;
+               case sizeof(int8):
+                       if (errno == ERANGE || l < SCHAR_MIN || l > SCHAR_MAX)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                               errmsg("value \"%s\" is out of range for 8-bit integer", s)));
+                       break;
+               default:
+                       elog(ERROR, "unsupported result size: %d", size);
        }
-       break;
-    default:
-       elog(WARN, "pg_atoi: invalid result size: %d", size);
-    }
-    return((int32) l);
+
+       /*
+        * Skip any trailing whitespace; if anything but whitespace remains before
+        * the terminating character, bail out
+        */
+       while (*badp && *badp != c && isspace((unsigned char) *badp))
+               badp++;
+
+       if (*badp && *badp != c)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for integer: \"%s\"",
+                                               s)));
+
+       return (int32) l;
 }
 
 /*
- *     itoa            - converts a short int to its string represention
+ * pg_itoa: converts a signed 16-bit integer to its string representation
+ *
+ * Caller must ensure that 'a' points to enough memory to hold the result
+ * (at least 7 bytes, counting a leading sign and trailing NUL).
  *
- *     Note:
- *             previously based on ~ingres/source/gutil/atoi.c
- *             now uses vendor's sprintf conversion
+ * It doesn't seem worth implementing this separately.
  */
 void
-itoa(int i, char *a)
+pg_itoa(int16 i, char *a)
 {
-    sprintf(a, "%hd", (short)i);
+       pg_ltoa((int32) i, a);
 }
 
 /*
- *     ltoa            - converts a long int to its string represention
+ * pg_ltoa: converts a signed 32-bit integer to its string representation
  *
- *     Note:
- *             previously based on ~ingres/source/gutil/atoi.c
- *             now uses vendor's sprintf conversion
+ * Caller must ensure that 'a' points to enough memory to hold the result
+ * (at least 12 bytes, counting a leading sign and trailing NUL).
  */
 void
-ltoa(int32 l, char *a)
+pg_ltoa(int32 value, char *a)
 {
-    sprintf(a, "%d", l);
-}
+       char       *start = a;
+       bool            neg = false;
 
-/*
- **  ftoa      - FLOATING POINT TO ASCII CONVERSION
- **
- **    CODE derived from ingres, ~ingres/source/gutil/ftoa.c
- **
- **    'Value' is converted to an ascii character string and stored
- **    into 'ascii'.  Ascii should have room for at least 'width' + 1
- **    characters.  'Width' is the width of the output field (max).
- **    'Prec' is the number of characters to put after the decimal
- **    point.  The format of the output string is controlled by
- **    'format'.
- **
- **    'Format' can be:
- **            e or E: "E" format output
- **            f or F:  "F" format output
- **            g or G:  "F" format output if it will fit, otherwise
- **                    use "E" format.
- **            n or N:  same as G, but decimal points will not always
- **                    be aligned.
- **
- **    If 'format' is upper case, the "E" comes out in upper case;
- **    otherwise it comes out in lower case.
- **
- **    When the field width is not big enough, it fills the field with
- **    stars ("*****") and returns zero.  Normal return is the width
- **    of the output field (sometimes shorter than 'width').
- */
-int
-ftoa(double value, char *ascii, int width, int prec1, char format)
-{
-#if defined(BSD44_derived) || \
-    defined(bsdi) || \
-    defined(bsdi_2_1)
-       char    out[256];
-       char    fmt[256];
-       int     ret;
-
-       (void) sprintf(fmt, "%%%d.%d%c", width, prec1, format);
-       (void) sprintf(out, fmt, value);
-       if ((ret = strlen(out)) > width) {
-               memset(ascii, '*', width - 2);
-               ascii[width] = 0;
-               return(0);
-       }
-       (void) strcpy(ascii, out);
-       return(ret);
-#else
-    auto int   expon;
-    auto int   sign;
-    register int       avail;
-    register char      *a;
-    register char      *p;
-    char               mode;
-    int                lowercase;
-    int                prec;
-/*    extern char      *ecvt(), *fcvt();*/
-    
-    prec = prec1;
-    mode = format;
-    lowercase = 'a' - 'A';
-    if (mode >= 'a')
-       mode -= 'a' - 'A';
-    else
-       lowercase = 0;
-    
-    if (mode != 'E') {
-       /* try 'F' style output */
-       p = fcvt(value, prec, &expon, &sign);
-       avail = width;
-       a = ascii;
-       
-       /* output sign */
-       if (sign) {
-           avail--;
-           *a++ = '-';
-       }
-       
-       /* output '0' before the decimal point */
-       if (expon <= 0) {
-           *a++ = '0';
-           avail--;
+       /*
+        * Avoid problems with the most negative integer not being representable
+        * as a positive integer.
+        */
+       if (value == (-2147483647 - 1))
+       {
+               memcpy(a, "-2147483648", 12);
+               return;
        }
-       
-       /* compute space length left after dec pt and fraction */
-       avail -= prec + 1;
-       if (mode == 'G')
-           avail -= 4;
-       
-       if (avail >= expon) {
-           
-           /* it fits.  output */
-           while (expon > 0) {
-               /* output left of dp */
-               expon--;
-               if (*p) {
-                   *a++ = *p++;
-               } else
-                   *a++ = '0';
-           }
-           
-           /* output fraction (right of dec pt) */
-           avail = expon;
-           goto frac_out;
+       else if (value < 0)
+       {
+               value = -value;
+               neg = true;
        }
-       /* won't fit; let's hope for G format */
-    }
-    
-    if (mode != 'F') {
-       /* try to do E style output */
-       p = ecvt(value, prec + 1, &expon, &sign);
-       avail = width - 5;
-       a = ascii;
-       
-       /* output the sign */
-       if (sign) {
-           *a++ = '-';
-           avail--;
-       }
-    }
-    
-    /* check for field too small */
-    if (mode == 'F' || avail < prec) {
-       /* sorry joker, you lose */
-       a = ascii;
-       for (avail = width; avail > 0; avail--)
-           *a++ = '*';
-       *a = 0;
-       return (0);
-    }
-    
-    /* it fits; output the number */
-    mode = 'E';
-    
-    /* output the LHS single digit */
-    *a++ = *p++;
-    expon--;
-    
-    /* output the rhs */
-    avail = 1;
-    
- frac_out:
-    *a++ = '.';
-    while (prec > 0) {
-       prec--;
-       if (avail < 0) {
-           avail++;
-           *a++ = '0';
-       } else {
-           if (*p)
-               *a++ = *p++;
-           else
-               *a++ = '0';
+
+       /* Compute the result string backwards. */
+       do
+       {
+               int32           remainder;
+               int32           oldval = value;
+
+               value /= 10;
+               remainder = oldval - value * 10;
+               *a++ = '0' + remainder;
+       } while (value != 0);
+
+       if (neg)
+               *a++ = '-';
+
+       /* Add trailing NUL byte, and back up 'a' to the last character. */
+       *a-- = '\0';
+
+       /* Reverse string. */
+       while (start < a)
+       {
+               char            swap = *start;
+
+               *start++ = *a;
+               *a-- = swap;
        }
-    }
-    
-    /* output the exponent */
-    if (mode == 'E') {
-       *a++ = 'E' + lowercase;
-       if (expon < 0) {
-           *a++ = '-';
-           expon = -expon;
-       } else
-           *a++ = '+';
-       *a++ = (expon / 10) % 10 + '0';
-       *a++ = expon % 10 + '0';
-    }
-    
-    /* output spaces on the end in G format */
-    if (mode == 'G') {
-       *a++ = ' ';
-       *a++ = ' ';
-       *a++ = ' ';
-       *a++ = ' ';
-    }
-    
-    /* finally, we can return */
-    *a = 0;
-    avail = a - ascii;
-    return (avail);
-#endif /* !BSD44_derived */
 }
 
 /*
- **   atof1    - ASCII TO FLOATING CONVERSION
- **
- **    CODE derived from ~ingres/source/gutil/atof.c
- **
- **    Converts the string 'str' to floating point and stores the
- **    result into the cell pointed to by 'val'.
- **
- **    The syntax which it accepts is pretty much what you would
- **    expect.  Basically, it is:
- **            {<sp>} [+|-] {<sp>} {<digit>} [.{digit}] {<sp>} [<exp>]
- **    where <exp> is "e" or "E" followed by an integer, <sp> is a
- **    space character, <digit> is zero through nine, [] is zero or
- **    one, and {} is zero or more.
- **
- **    Parameters:
- **            str -- string to convert.
- **            val -- pointer to place to put the result (which
- **                    must be type double).
- **
- **    Returns:
- **            zero -- ok.
- **            -1 -- syntax error.
- **            +1 -- overflow (not implemented).
- **
- **    Side Effects:
- **            clobbers *val.
+ * pg_lltoa: convert a signed 64-bit integer to its string representation
+ *
+ * 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
-atof1(char *str, double *val)
+void
+pg_lltoa(int64 value, char *a)
 {
-    register char      *p;
-    double             v;
-    double             fact;
-    int                minus;
-    register char      c;
-    int                expon;
-    register int       gotmant;
-    
-    v = 0.0;
-    p = str;
-    minus = 0;
-    
-    /* skip leading blanks */
-    while ((c = *p) != '\0') {
-       if (c != ' ')
-           break;
-       p++;
-    }
-    
-    /* handle possible sign */
-    switch (c) {
-    case '-':
-       minus++;
-       
-    case '+':
-       p++;
-    }
-    
-    /* skip blanks after sign */
-    while ((c = *p) != '\0') {
-       if (c != ' ')
-           break;
-       p++;
-    }
-    
-    /* start collecting the number to the decimal point */
-    gotmant = 0;
-    for (;;) {
-       c = *p;
-       if (c < '0' || c > '9')
-           break;
-       v = v * 10.0 + (c - '0');
-       gotmant++;
-       p++;
-    }
-    
-    /* check for fractional part */
-    if (c == '.') {
-       fact = 1.0;
-       for (;;) {
-           c = *++p;
-           if (c < '0' || c > '9')
-               break;
-           fact *= 0.1;
-           v += (c - '0') * fact;
-           gotmant++;
+       char       *start = a;
+       bool            neg = false;
+
+       /*
+        * Avoid problems with the most negative integer not being representable
+        * as a positive integer.
+        */
+       if (value == (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1))
+       {
+               memcpy(a, "-9223372036854775808", 21);
+               return;
+       }
+       else if (value < 0)
+       {
+               value = -value;
+               neg = true;
+       }
+
+       /* Compute the result string backwards. */
+       do
+       {
+               int64           remainder;
+               int64           oldval = value;
+
+               value /= 10;
+               remainder = oldval - value * 10;
+               *a++ = '0' + remainder;
+       } while (value != 0);
+
+       if (neg)
+               *a++ = '-';
+
+       /* Add trailing NUL byte, and back up 'a' to the last character. */
+       *a-- = '\0';
+
+       /* Reverse string. */
+       while (start < a)
+       {
+               char            swap = *start;
+
+               *start++ = *a;
+               *a-- = swap;
        }
-    }
-    
-    /* skip blanks before possible exponent */
-    while ((c = *p) != '\0') {
-       if (c != ' ')
-           break;
-       p++;
-    }
-    
-    /* test for exponent */
-    if (c == 'e' || c == 'E') {
-       p++;
-       expon = pg_atoi(p, sizeof(expon), '\0');
-       if (!gotmant)
-           v = 1.0;
-       fact = expon;
-       v *= pow(10.0, fact);
-    } else {
-       /* if no exponent, then nothing */
-       if (c != 0)
-           return (-1);
-    }
-    
-    /* store the result and exit */
-    if (minus)
-       v = -v;
-    *val = v;
-    return (0);
 }