Skip to content

Commit 4fc115b

Browse files
committed
Speed up conversion of signed integers to C strings.
A hand-coded implementation turns out to be much faster than calling printf(). In passing, add a few more regresion tests. Andres Freund, with assorted, mostly cosmetic changes.
1 parent 0f61d4d commit 4fc115b

File tree

9 files changed

+157
-18
lines changed

9 files changed

+157
-18
lines changed

src/backend/utils/adt/int8.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "funcapi.h"
2121
#include "libpq/pqformat.h"
2222
#include "utils/int8.h"
23+
#include "utils/builtins.h"
2324

2425

2526
#define MAXINT8LEN 25
@@ -157,13 +158,10 @@ Datum
157158
int8out(PG_FUNCTION_ARGS)
158159
{
159160
int64 val = PG_GETARG_INT64(0);
160-
char *result;
161-
int len;
162161
char buf[MAXINT8LEN + 1];
162+
char *result;
163163

164-
if ((len = snprintf(buf, MAXINT8LEN, INT64_FORMAT, val)) < 0)
165-
elog(ERROR, "could not format int8");
166-
164+
pg_lltoa(val, buf);
167165
result = pstrdup(buf);
168166
PG_RETURN_CSTRING(result);
169167
}

src/backend/utils/adt/numutils.c

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
* numutils.c
44
* utility functions for I/O of built-in numeric types.
55
*
6-
* integer: pg_atoi, pg_itoa, pg_ltoa
7-
*
86
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
97
* Portions Copyright (c) 1994, Regents of the University of California
108
*
@@ -109,27 +107,118 @@ pg_atoi(char *s, int size, int c)
109107
}
110108

111109
/*
112-
* pg_itoa - converts a short int to its string represention
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).
113114
*
114-
* Note:
115-
* previously based on ~ingres/source/gutil/atoi.c
116-
* now uses vendor's sprintf conversion
115+
* It doesn't seem worth implementing this separately.
117116
*/
118117
void
119118
pg_itoa(int16 i, char *a)
120119
{
121-
sprintf(a, "%hd", (short) i);
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 == INT32_MIN)
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 backwards. */
151+
do
152+
{
153+
int32 remainder;
154+
int32 oldval = value;
155+
value /= 10;
156+
remainder = oldval - value * 10;
157+
*a++ = '0' + remainder;
158+
} while (value != 0);
159+
if (neg)
160+
*a++ = '-';
161+
162+
/* Add trailing NUL byte. */
163+
*a-- = '\0';
164+
165+
/* reverse string */
166+
while (start < a)
167+
{
168+
char swap = *start;
169+
*start++ = *a;
170+
*a-- = swap;
171+
}
122172
}
123173

124174
/*
125-
* pg_ltoa - converts a long int to its string represention
175+
* pg_lltoa: convert a signed 64bit integer to its string representation
126176
*
127-
* Note:
128-
* previously based on ~ingres/source/gutil/atoi.c
129-
* now uses vendor's sprintf conversion
177+
* Caller must ensure that 'a' points to enough memory to hold the result
178+
* (at least MAXINT8LEN+1 bytes, counting a leading sign and trailing NUL).
130179
*/
131180
void
132-
pg_ltoa(int32 l, char *a)
181+
pg_lltoa(int64 value, char *a)
133182
{
134-
sprintf(a, "%d", l);
183+
char *start = a;
184+
bool neg = false;
185+
186+
/*
187+
* Avoid problems with the most negative integer not being representable
188+
* as a positive integer.
189+
*/
190+
if (value == INT64_MIN)
191+
{
192+
memcpy(a, "-9223372036854775808", 21);
193+
return;
194+
}
195+
else if (value < 0)
196+
{
197+
value = -value;
198+
neg = true;
199+
}
200+
201+
/* Build the string by computing the wanted string backwards. */
202+
do
203+
{
204+
int64 remainder;
205+
int64 oldval = value;
206+
value /= 10;
207+
remainder = oldval - value * 10;
208+
*a++ = '0' + remainder;
209+
} while (value != 0);
210+
211+
if (neg)
212+
*a++ = '-';
213+
214+
/* Add trailing NUL byte. */
215+
*a-- = '\0';
216+
217+
/* Reverse string. */
218+
while (start < a)
219+
{
220+
char swap = *start;
221+
*start++ = *a;
222+
*a-- = swap;
223+
}
135224
}

src/include/utils/builtins.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ extern Datum current_schemas(PG_FUNCTION_ARGS);
275275
extern int32 pg_atoi(char *s, int size, int c);
276276
extern void pg_itoa(int16 i, char *a);
277277
extern void pg_ltoa(int32 l, char *a);
278+
extern void pg_lltoa(int64 ll, char *a);
278279

279280
/*
280281
* Per-opclass comparison functions for new btrees. These are

src/test/regress/expected/int2.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,16 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
242242
| -32767 | -16383
243243
(5 rows)
244244

245+
-- corner cases
246+
SELECT (1<<15-1)::int2::text;
247+
text
248+
-------
249+
16384
250+
(1 row)
251+
252+
SELECT (-1<<15)::int2::text;
253+
text
254+
--------
255+
-32768
256+
(1 row)
257+

src/test/regress/expected/int4.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,16 @@ SELECT (2 + 2) / 2 AS two;
329329
2
330330
(1 row)
331331

332+
-- corner cases
333+
SELECT (1<<31-1)::int4::text;
334+
text
335+
------------
336+
1073741824
337+
(1 row)
338+
339+
SELECT (1<<31)::int4::text;
340+
text
341+
-------------
342+
-2147483648
343+
(1 row)
344+

src/test/regress/expected/int8.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,3 +802,16 @@ SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::in
802802
4567890123456799
803803
(6 rows)
804804

805+
-- corner cases
806+
SELECT (1<<63-1)::int8::text;
807+
text
808+
------------
809+
1073741824
810+
(1 row)
811+
812+
SELECT (1<<63)::int8::text;
813+
text
814+
-------------
815+
-2147483648
816+
(1 row)
817+

src/test/regress/sql/int2.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@ SELECT '' AS five, i.f1, i.f1 - int4 '2' AS x FROM INT2_TBL i;
8383
SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
8484

8585
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
86+
87+
-- corner cases
88+
SELECT (1<<15-1)::int2::text;
89+
SELECT (-1<<15)::int2::text;

src/test/regress/sql/int4.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,7 @@ SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten;
123123
SELECT 2 + 2 / 2 AS three;
124124

125125
SELECT (2 + 2) / 2 AS two;
126+
127+
-- corner cases
128+
SELECT (1<<31-1)::int4::text;
129+
SELECT (1<<31)::int4::text;

src/test/regress/sql/int8.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,7 @@ SELECT q1, q1 << 2 AS "shl", q1 >> 3 AS "shr" FROM INT8_TBL;
190190
SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8);
191191
SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8, 0);
192192
SELECT * FROM generate_series('+4567890123456789'::int8, '+4567890123456799'::int8, 2);
193+
194+
-- corner cases
195+
SELECT (1<<63-1)::int8::text;
196+
SELECT (1<<63)::int8::text;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy