]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/numutils.c
Assorted further cleanup for integer-conversion patch.
[postgresql] / src / backend / utils / adt / numutils.c
1 /*-------------------------------------------------------------------------
2  *
3  * numutils.c
4  *        utility functions for I/O of built-in numeric types.
5  *
6  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        src/backend/utils/adt/numutils.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <math.h>
18 #include <limits.h>
19 #include <ctype.h>
20
21 #include "utils/builtins.h"
22
23 /*
24  * pg_atoi: convert string to integer
25  *
26  * allows any number of leading or trailing whitespace characters.
27  *
28  * 'size' is the sizeof() the desired integral result (1, 2, or 4 bytes).
29  *
30  * c, if not 0, is a terminator character that may appear after the
31  * integer (plus whitespace).  If 0, the string must end after the integer.
32  *
33  * Unlike plain atoi(), this will throw ereport() upon bad input format or
34  * overflow.
35  */
36 int32
37 pg_atoi(char *s, int size, int c)
38 {
39         long            l;
40         char       *badp;
41
42         /*
43          * Some versions of strtol treat the empty string as an error, but some
44          * seem not to.  Make an explicit test to be sure we catch it.
45          */
46         if (s == NULL)
47                 elog(ERROR, "NULL pointer");
48         if (*s == 0)
49                 ereport(ERROR,
50                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
51                                  errmsg("invalid input syntax for integer: \"%s\"",
52                                                 s)));
53
54         errno = 0;
55         l = strtol(s, &badp, 10);
56
57         /* We made no progress parsing the string, so bail out */
58         if (s == badp)
59                 ereport(ERROR,
60                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
61                                  errmsg("invalid input syntax for integer: \"%s\"",
62                                                 s)));
63
64         switch (size)
65         {
66                 case sizeof(int32):
67                         if (errno == ERANGE
68 #if defined(HAVE_LONG_INT_64)
69                         /* won't get ERANGE on these with 64-bit longs... */
70                                 || l < INT_MIN || l > INT_MAX
71 #endif
72                                 )
73                                 ereport(ERROR,
74                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
75                                 errmsg("value \"%s\" is out of range for type integer", s)));
76                         break;
77                 case sizeof(int16):
78                         if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
79                                 ereport(ERROR,
80                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
81                                 errmsg("value \"%s\" is out of range for type smallint", s)));
82                         break;
83                 case sizeof(int8):
84                         if (errno == ERANGE || l < SCHAR_MIN || l > SCHAR_MAX)
85                                 ereport(ERROR,
86                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
87                                 errmsg("value \"%s\" is out of range for 8-bit integer", s)));
88                         break;
89                 default:
90                         elog(ERROR, "unsupported result size: %d", size);
91         }
92
93         /*
94          * Skip any trailing whitespace; if anything but whitespace remains before
95          * the terminating character, bail out
96          */
97         while (*badp && *badp != c && isspace((unsigned char) *badp))
98                 badp++;
99
100         if (*badp && *badp != c)
101                 ereport(ERROR,
102                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
103                                  errmsg("invalid input syntax for integer: \"%s\"",
104                                                 s)));
105
106         return (int32) l;
107 }
108
109 /*
110  * pg_itoa: converts a signed 16-bit integer to its string representation
111  *
112  * Caller must ensure that 'a' points to enough memory to hold the result
113  * (at least 7 bytes, counting a leading sign and trailing NUL).
114  *
115  * It doesn't seem worth implementing this separately.
116  */
117 void
118 pg_itoa(int16 i, char *a)
119 {
120         pg_ltoa((int32) i, a);
121 }
122
123 /*
124  * pg_ltoa: converts a signed 32-bit integer to its string representation
125  *
126  * Caller must ensure that 'a' points to enough memory to hold the result
127  * (at least 12 bytes, counting a leading sign and trailing NUL).
128  */
129 void
130 pg_ltoa(int32 value, char *a)
131 {
132         char       *start = a;
133         bool            neg = false;
134
135         /*
136          * Avoid problems with the most negative integer not being representable
137          * as a positive integer.
138          */
139         if (value == (-2147483647-1))
140         {
141                 memcpy(a, "-2147483648", 12);
142                 return;
143         }
144         else if (value < 0)
145         {
146                 value = -value;
147                 neg = true;
148         }
149
150         /* Compute the result string backwards. */
151         do
152         {
153                 int32   remainder;
154                 int32   oldval = value;
155
156                 value /= 10;
157                 remainder = oldval - value * 10;
158                 *a++ = '0' + remainder;
159         } while (value != 0);
160
161         if (neg)
162                 *a++ = '-';
163
164         /* Add trailing NUL byte, and back up 'a' to the last character. */
165         *a-- = '\0';
166
167         /* Reverse string. */
168         while (start < a)
169         {
170                 char    swap = *start;
171
172                 *start++ = *a;
173                 *a-- = swap;
174         }
175 }
176
177 /*
178  * pg_lltoa: convert a signed 64-bit integer to its string representation
179  *
180  * Caller must ensure that 'a' points to enough memory to hold the result
181  * (at least MAXINT8LEN+1 bytes, counting a leading sign and trailing NUL).
182  */
183 void
184 pg_lltoa(int64 value, char *a)
185 {
186         char       *start = a;
187         bool            neg = false;
188
189         /*
190          * Avoid problems with the most negative integer not being representable
191          * as a positive integer.
192          */
193         if (value == (-INT64CONST(0x7FFFFFFFFFFFFFFF)-1))
194         {
195                 memcpy(a, "-9223372036854775808", 21);
196                 return;
197         }
198         else if (value < 0)
199         {
200                 value = -value;
201                 neg = true;
202         }
203
204         /* Compute the result string backwards. */
205         do
206         {
207                 int64   remainder;
208                 int64   oldval = value;
209
210                 value /= 10;
211                 remainder = oldval - value * 10;
212                 *a++ = '0' + remainder;
213         } while (value != 0);
214
215         if (neg)
216                 *a++ = '-';
217
218         /* Add trailing NUL byte, and back up 'a' to the last character. */
219         *a-- = '\0';
220
221         /* Reverse string. */
222         while (start < a)
223         {
224                 char    swap = *start;
225
226                 *start++ = *a;
227                 *a-- = swap;
228         }
229 }